summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Application.vala109
-rw-r--r--src/BatchImport.vala2
-rw-r--r--src/CheckerboardLayout.vala4
-rw-r--r--src/DesktopIntegration.vala22
-rw-r--r--src/Resources.vala2
-rw-r--r--src/authenticator.vala40
-rw-r--r--src/direct/DirectWindow.vala5
-rw-r--r--src/folders/FoldersBranch.vala2
-rw-r--r--src/import-roll/ImportRollBranch.vala2
-rw-r--r--src/main.vala8
-rw-r--r--src/meson.build14
-rw-r--r--src/photos/WebPSupport.vala6
-rw-r--r--src/plugins/PublishingInterfaces.vala7
-rw-r--r--src/publishing/PublishingPluginHost.vala8
-rw-r--r--src/threads/Workers.vala22
-rw-r--r--src/util/image.vala2
16 files changed, 218 insertions, 37 deletions
diff --git a/src/Application.vala b/src/Application.vala
index 59bae36..d9edcaf 100644
--- a/src/Application.vala
+++ b/src/Application.vala
@@ -4,7 +4,75 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
+[DBus(name = "org.gnome.Shotwell.Authenticate")]
+public interface AuthenticationReceiver : Object {
+ public abstract void callback(string url) throws DBusError, IOError;
+}
+
+[DBus(name = "org.gnome.Shotwell.Authenticate")]
+internal class AuthenticatorReceiverApp : Gtk.Application, AuthenticationReceiver {
+ private Gee.HashMap<string, Spit.Publishing.AuthenticatedCallback>
+ pending_auth_requests = new Gee.HashMap<string, Spit.Publishing.AuthenticatedCallback>();
+
+ public AuthenticatorReceiverApp() {
+ Object(application_id: "org.gnome.Shotwell", flags: GLib.ApplicationFlags.HANDLES_OPEN |
+ GLib.ApplicationFlags.HANDLES_COMMAND_LINE);
+ }
+ public override bool dbus_register(DBusConnection connection, string object_path) throws Error {
+ try {
+ connection.register_object(object_path, this);
+ } catch (IOError e) {
+ warning("Failed to register authentication helper on session connection: %s", e.message);
+ }
+ return true;
+ }
+
+
+ internal void register_auth_callback(string cookie, Spit.Publishing.AuthenticatedCallback cb) {
+ pending_auth_requests[cookie] = cb;
+ }
+
+ internal void unregister_auth_callback(string cookie) {
+ pending_auth_requests.unset(cookie);
+ }
+
+ public void callback(string callback_url) throws DBusError, IOError {
+ try {
+ var uri = Uri.parse(callback_url, UriFlags.NONE);
+ debug("Got authentication callback uri: %s", callback_url);
+ // See if something is waiting for a pending authentication
+ var query = uri.get_query();
+ if (query == null || query == "") {
+ debug("Callback does not have parameters. Not accepting");
+
+ return;
+ }
+ var uri_params = Uri.parse_params(uri.get_query());
+ if ("sw_auth_cookie" in uri_params) {
+ var cookie = uri_params["sw_auth_cookie"];
+ if (pending_auth_requests.has_key(cookie)) {
+ pending_auth_requests[cookie].authenticated(uri_params);
+ LibraryWindow.get_app().present();
+ } else {
+ debug("No call-back registered for cookie %s, probably user cancelled", cookie);
+ }
+ } else if (uri.get_scheme().has_prefix("com.googleusercontent.apps")) {
+ if (pending_auth_requests.has_key(uri.get_scheme())) {
+ pending_auth_requests[uri.get_scheme()].authenticated(uri_params);
+ } else {
+ debug("No call-back registered for cookie %s, probably user cancelled", uri.get_scheme());
+ }
+ }
+ } catch (Error error) {
+ warning("Got invalid authentication call-back: %s", callback_url);
+ }
+ }
+}
+
public class Application {
+ public interface AuthCallback : Object {
+ public abstract void authenticated(HashTable<string, string> params);
+ }
private static Application instance = null;
private Gtk.Application system_app = null;
private int system_app_run_retval = 0;
@@ -36,20 +104,21 @@ public class Application {
private bool running = false;
private bool exiting_fired = false;
+ Gee.HashMap<string, AuthCallback> pending_auth_requests = new Gee.HashMap<string, AuthCallback>();
+
private Application(bool is_direct) {
if (is_direct) {
// we allow multiple instances of ourself in direct mode, so DON'T
// attempt to be unique. We don't request any command-line handling
// here because this is processed elsewhere, and we don't need to handle
// command lines from remote instances, since we don't care about them.
- system_app = new Gtk.Application("org.gnome.Shotwell-direct", GLib.ApplicationFlags.HANDLES_OPEN |
+ system_app = new Gtk.Application("org.gnome.Shotwell-Viewer", GLib.ApplicationFlags.HANDLES_OPEN |
GLib.ApplicationFlags.NON_UNIQUE);
} else {
// we've been invoked in library mode; set up for uniqueness and handling
// of incoming command lines from remote instances (needed for getting
// storage device and camera mounts).
- system_app = new Gtk.Application("org.gnome.Shotwell", GLib.ApplicationFlags.HANDLES_OPEN |
- GLib.ApplicationFlags.HANDLES_COMMAND_LINE);
+ system_app = new AuthenticatorReceiverApp();
}
// GLib will assert if we don't do this...
@@ -63,12 +132,46 @@ public class Application {
if (!direct) {
system_app.command_line.connect(on_command_line);
+ var action = new SimpleAction("authenticated", VariantType.STRING);
+ system_app.add_action(action);
+ action.activate.connect((a, p) => {
+ try {
+ var uri = Uri.parse(p.get_string(), UriFlags.NONE);
+ debug("Got authentication callback uri: %s", p.get_string());
+ // See if something is waiting for a pending authentication
+ var uri_params = Uri.parse_params(uri.get_query());
+ if ("sw_auth_cookie" in uri_params) {
+ var cookie = uri_params["sw_auth_cookie"];
+ if (pending_auth_requests.has_key(cookie)) {
+ pending_auth_requests[cookie].authenticated(uri_params);
+ } else {
+ debug("No call-back registered for cookie %s, probably user cancelled", cookie);
+ }
+ }
+ } catch (Error error) {
+ warning("Got invalid authentication call-back: %s", p.get_string());
+ }
+ });
}
system_app.activate.connect(on_activated);
system_app.startup.connect(on_activated);
}
+ public static void register_auth_callback(string cookie, Spit.Publishing.AuthenticatedCallback cb) {
+ var instance = get_instance();
+ if (!instance.direct) {
+ ((AuthenticatorReceiverApp)instance.system_app).register_auth_callback(cookie, cb);
+ }
+ }
+
+ public static void unregister_auth_callback(string cookie) {
+ var instance = get_instance();
+ if (!instance.direct) {
+ ((AuthenticatorReceiverApp)instance.system_app).unregister_auth_callback(cookie);
+ }
+ }
+
public static double get_scale() {
var instance = get_instance().system_app;
unowned GLib.List<Gtk.Window> windows = instance.get_windows();
diff --git a/src/BatchImport.vala b/src/BatchImport.vala
index ae4f573..9b3e1e6 100644
--- a/src/BatchImport.vala
+++ b/src/BatchImport.vala
@@ -1851,7 +1851,7 @@ private class PrepareFilesJob : BackgroundImportJob {
warning("Unable to perform MD5 checksum on file %s: %s", file.get_path(),
err.message);
- return ImportResult.convert_error(err, ImportResult.FILE_ERROR);
+ return ImportResult.FILE_ERROR;
}
// we only care about file extensions and metadata if we're importing a photo --
diff --git a/src/CheckerboardLayout.vala b/src/CheckerboardLayout.vala
index 85232f3..a22412d 100644
--- a/src/CheckerboardLayout.vala
+++ b/src/CheckerboardLayout.vala
@@ -1070,10 +1070,10 @@ public class CheckerboardLayout : Gtk.DrawingArea {
ctx.save();
ctx.add_class("view");
var val = ctx.get_property("border-color", Gtk.StateFlags.NORMAL);
- focus_color = *(Gdk.RGBA*)val.get_boxed();
+ border_color = *(Gdk.RGBA*)val.get_boxed();
val = ctx.get_property("border-color", Gtk.StateFlags.FOCUSED);
- border_color = *(Gdk.RGBA*)val.get_boxed();
+ focus_color = *(Gdk.RGBA*)val.get_boxed();
// Checked in GtkIconView - The selection is drawn using render_background
val = ctx.get_property("background-color", Gtk.StateFlags.FOCUSED | Gtk.StateFlags.SELECTED);
diff --git a/src/DesktopIntegration.vala b/src/DesktopIntegration.vala
index 754d9a1..d29e0f7 100644
--- a/src/DesktopIntegration.vala
+++ b/src/DesktopIntegration.vala
@@ -105,13 +105,13 @@ public async void files_send_to(File[] files) {
}
AppWindow.get_instance().set_busy_cursor();
- try{
- var portal = new Xdp.Portal();
+ try {
+ var portal = new Xdp.Portal.initable_new();
// Use empty list for addresses instead of null to word around bug in xdg-desktop-portal-gtk
yield portal.compose_email(parent, {null}, null, null,
_("Send files per Mail: ") + file_names.str, null, file_paths, Xdp.EmailFlags.NONE, null);
- } catch (Error e){
+ } catch (Error e) {
// Translators: The first %s is the name of the file, the second %s is the reason why it could not be sent
AppWindow.error_message(_("Unable to send file %s, %s").printf(
file_names.str, e.message));
@@ -175,12 +175,16 @@ public void set_background(Photo photo, bool desktop, bool screensaver) {
}
var parent = Xdp.parent_new_gtk(AppWindow.get_instance());
- var portal = new Xdp.Portal();
Xdp.WallpaperFlags flags = Xdp.WallpaperFlags.PREVIEW;
if (desktop) flags |= Xdp.WallpaperFlags.BACKGROUND;
if (screensaver) flags |= Xdp.WallpaperFlags.LOCKSCREEN;
- portal.set_wallpaper.begin(parent, save_as.get_uri(), flags, null);
+ try {
+ var portal = new Xdp.Portal.initable_new();
+ portal.set_wallpaper.begin(parent, save_as.get_uri(), flags, null);
+ } catch (Error err) {
+ AppWindow.error_message(_("Unable to set background: %s").printf(err.message));
+ }
GLib.FileUtils.chmod(save_as.get_parse_name(), 0644);
}
@@ -313,12 +317,16 @@ private void on_desktop_slideshow_exported(Exporter exporter, bool is_cancelled)
}
var parent = Xdp.parent_new_gtk(AppWindow.get_instance());
- var portal = new Xdp.Portal();
Xdp.WallpaperFlags flags = Xdp.WallpaperFlags.PREVIEW;
if (set_desktop_background) flags |= Xdp.WallpaperFlags.BACKGROUND;
if (set_screensaver) flags |= Xdp.WallpaperFlags.LOCKSCREEN;
- portal.set_wallpaper.begin(parent, xml_file.get_uri(), flags, null);
+ try {
+ var portal = new Xdp.Portal.initable_new();
+ portal.set_wallpaper.begin(parent, xml_file.get_uri(), flags, null);
+ } catch (Error err) {
+ AppWindow.error_message(_("Unable to set background: %s").printf(err.message));
+ }
}
}
diff --git a/src/Resources.vala b/src/Resources.vala
index d03a214..0bd8512 100644
--- a/src/Resources.vala
+++ b/src/Resources.vala
@@ -853,6 +853,7 @@ along with Shotwell; if not, write to the Free Software Foundation, Inc.,
/// Locale-specific starting date format for multi-date strings,
/// i.e. the "Tue Mar 08" in "Tue Mar 08 - 10, 2006"
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
+ /// xgettext:no-c-format
START_MULTIDAY_DATE_FORMAT_STRING = C_("MultidayFormat", "%a %b %d");
/// Locale-specific ending date format for multi-date strings,
@@ -863,6 +864,7 @@ along with Shotwell; if not, write to the Free Software Foundation, Inc.,
/// Locale-specific calendar date format for multi-month strings,
/// i.e. the "Tue Mar 08" in "Tue Mar 08 to Mon Apr 06, 2006"
/// See http://developer.gnome.org/glib/2.32/glib-GDateTime.html#g-date-time-format
+ /// xgettext:no-c-format
START_MULTIMONTH_DATE_FORMAT_STRING = C_("MultimonthFormat", "%a %b %d");
/// Locale-specific calendar date format for multi-month strings,
diff --git a/src/authenticator.vala b/src/authenticator.vala
new file mode 100644
index 0000000..55f3321
--- /dev/null
+++ b/src/authenticator.vala
@@ -0,0 +1,40 @@
+// SPDX-License-Identifer: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2022 Jens Georg <mail@jensge.org>
+
+[DBus(name = "org.gnome.Shotwell.Authenticate")]
+public interface AuthenticationReceiver : Object {
+ public abstract void callback(string url) throws DBusError, IOError;
+}
+
+static int main(string[] args) {
+ AuthenticationReceiver receiver;
+
+ if (args.length != 2) {
+ print("Usage: %s <callback-uri>\n", args[0]);
+ return 1;
+ }
+
+ try {
+ var uri = Uri.parse(args[1], UriFlags.NONE);
+ var scheme = uri.get_scheme();
+
+ if (scheme != "shotwell-oauth2" && !scheme.has_prefix("com.googleusercontent.apps")) {
+ critical("Invalid scheme in callback URI \"%s\"", args[1]);
+ return 1;
+ }
+ } catch (Error e) {
+ critical("Invalid uri: \"%s\": %s", args[1], e.message);
+ return 1;
+ }
+
+ try {
+ receiver = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.Shotwell", "/org/gnome/Shotwell");
+ receiver.callback(args[1]);
+ } catch (Error e) {
+ critical("Could not connect to remote shotwell instance: %s", e.message);
+
+ return 1;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/src/direct/DirectWindow.vala b/src/direct/DirectWindow.vala
index baf6124..d39e83d 100644
--- a/src/direct/DirectWindow.vala
+++ b/src/direct/DirectWindow.vala
@@ -40,7 +40,7 @@ public class DirectWindow : AppWindow {
}
public void update_title(File file, bool modified) {
- title = "%s%s (%s) - %s".printf((modified) ? "*" : "", file.get_basename(),
+ title = "%s%s (%s) - %s".printf((modified) ? "•" : "", file.get_basename(),
get_display_pathname(file.get_parent()), Resources.APP_TITLE);
}
@@ -66,6 +66,9 @@ public class DirectWindow : AppWindow {
}
protected override void on_quit() {
+ if (!get_direct_page().check_quit())
+ return;
+
Config.Facade.get_instance().set_direct_window_state(maximized, dimensions);
base.on_quit();
diff --git a/src/folders/FoldersBranch.vala b/src/folders/FoldersBranch.vala
index 49b2d97..bfa461d 100644
--- a/src/folders/FoldersBranch.vala
+++ b/src/folders/FoldersBranch.vala
@@ -9,7 +9,7 @@ public class Folders.Branch : Sidebar.Branch {
new Gee.HashMap<File, Folders.SidebarEntry>(file_hash, file_equal);
private File home_dir;
- public class Branch() {
+ public Branch() {
base (new Folders.Root(),
Sidebar.Branch.Options.STARTUP_OPEN_GROUPING
| Sidebar.Branch.Options.HIDE_IF_EMPTY,
diff --git a/src/import-roll/ImportRollBranch.vala b/src/import-roll/ImportRollBranch.vala
index 0c582ac..dd3edfa 100644
--- a/src/import-roll/ImportRollBranch.vala
+++ b/src/import-roll/ImportRollBranch.vala
@@ -1,7 +1,7 @@
public class ImportRoll.Branch : Sidebar.Branch {
private Gee.HashMap<int64?, ImportRoll.SidebarEntry> entries;
- public class Branch() {
+ public Branch() {
base (new ImportRoll.Root(),
Sidebar.Branch.Options.HIDE_IF_EMPTY,
ImportRoll.Branch.comparator);
diff --git a/src/main.vala b/src/main.vala
index d07b7f5..25a0690 100644
--- a/src/main.vala
+++ b/src/main.vala
@@ -373,7 +373,6 @@ void editing_exec(string filename, bool fullscreen) {
DirectWindow direct_window = new DirectWindow(initial_file);
direct_window.show_all();
- direct_window.maximize();
debug("%lf seconds to Gtk.main()", startup_timer.elapsed());
@@ -572,9 +571,12 @@ void main(string[] args) {
Application.init(!is_string_empty(filename));
// set custom data directory if it's been supplied
- if (CommandlineOptions.data_dir != null)
+ if (CommandlineOptions.data_dir != null) {
+ if (CommandlineOptions.profile == null) {
+ AppWindow.error_message("Using the --datadir option without passing --profile and --create is deprecated\n. Plesae migrate to a proper profile instead.");
+ }
AppDirs.set_data_dir(CommandlineOptions.data_dir);
- else
+ } else
AppDirs.try_migrate_data();
// Verify the private data directory before continuing
diff --git a/src/meson.build b/src/meson.build
index 460092e..25f967a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -12,17 +12,25 @@ sw_graphics_processor = static_library('shotwell-graphics-processor',
vala_args : '--disable-assert',
install : false)
-processor = executable('shotwell-graphics-processor',
+executable('shotwell-graphics-processor',
['graphics-processor.vala'],
dependencies: [gio, gdk, gee],
link_with: sw_graphics_processor)
+executable('shotwell-authenticator',
+ [
+ 'authenticator.vala'
+ ],
+ dependencies: [gio],
+ include_directories: config_incdir,
+ install: true,
+ install_dir : join_paths(get_option('libexecdir'), 'shotwell')
+)
+
shotwell_deps = [gio, gee, sqlite, gtk, sqlite, posix, gphoto2,
gstreamer_pbu, gudev, gexiv2, gmodule,
libraw, libexif, sw_plugin]
-shotwell_libs = [sw_graphics_processor]
-
face_sources = (['faces/FacesBranch.vala',
'faces/FacePage.vala',
'faces/FaceShape.vala',
diff --git a/src/photos/WebPSupport.vala b/src/photos/WebPSupport.vala
index 2f4723c..b467b24 100644
--- a/src/photos/WebPSupport.vala
+++ b/src/photos/WebPSupport.vala
@@ -183,7 +183,13 @@ private class WebpSniffer : PhotoFileSniffer {
if (calc_md5)
detected.md5 = md5_checksum.get_string();
+ // We have never reached the header parsing state, but also didn't encounter any error
+ if (detected.file_format != PhotoFileFormat.WEBP) {
+ return null;
+ }
+
return detected;
+
}
}
diff --git a/src/plugins/PublishingInterfaces.vala b/src/plugins/PublishingInterfaces.vala
index 05b161f..84cb943 100644
--- a/src/plugins/PublishingInterfaces.vala
+++ b/src/plugins/PublishingInterfaces.vala
@@ -92,6 +92,9 @@ public errordomain PublishingError {
SSL_FAILED
}
+public interface AuthenticatedCallback : Object {
+ public abstract void authenticated(HashTable<string, string> params);
+}
/**
* Represents a connection to a publishing service.
*
@@ -503,6 +506,10 @@ public interface PluginHost : GLib.Object, Spit.HostInterface {
*/
public abstract Spit.Publishing.Publisher.MediaType get_publishable_media_type();
+
+ public abstract void register_auth_callback(string cookie, AuthenticatedCallback callback);
+ public abstract void unregister_auth_callback(string cookie);
+
//
// For future expansion.
//
diff --git a/src/publishing/PublishingPluginHost.vala b/src/publishing/PublishingPluginHost.vala
index 7804924..88b99e7 100644
--- a/src/publishing/PublishingPluginHost.vala
+++ b/src/publishing/PublishingPluginHost.vala
@@ -33,6 +33,14 @@ public class ConcretePublishingHost : Plugins.StandardHostInterface,
this.active_publisher = service.create_publisher_with_account(this, account);
}
+ public void register_auth_callback(string cookie, AuthenticatedCallback callback) {
+ Application.register_auth_callback(cookie, callback);
+ }
+
+ public void unregister_auth_callback(string cookie) {
+ Application.unregister_auth_callback(cookie);
+ }
+
public string get_current_profile_id() {
return Shotwell.ProfileManager.get_instance().id();
}
diff --git a/src/threads/Workers.vala b/src/threads/Workers.vala
index 60751a9..42d696c 100644
--- a/src/threads/Workers.vala
+++ b/src/threads/Workers.vala
@@ -18,7 +18,6 @@ public class Workers {
private ThreadPool<void *> thread_pool;
private AsyncQueue<BackgroundJob> queue = new AsyncQueue<BackgroundJob>();
private EventSemaphore empty_event = new EventSemaphore();
- private int enqueued = 0;
public Workers(uint max_threads, bool exclusive) {
if (max_threads <= 0 && max_threads != UNLIMITED_THREADS)
@@ -51,10 +50,7 @@ public class Workers {
public void enqueue(BackgroundJob job) {
empty_event.reset();
- lock (queue) {
- queue.push_sorted(job, BackgroundJob.priority_compare_func);
- enqueued++;
- }
+ queue.push_sorted(job, BackgroundJob.priority_compare_func);
try {
thread_pool.add(job);
@@ -76,21 +72,19 @@ public class Workers {
// Returns the number of BackgroundJobs on the queue, not including active jobs.
public int get_pending_job_count() {
- lock (queue) {
- return enqueued;
- }
+ return queue.length();
}
private void thread_start(void *ignored) {
BackgroundJob? job;
bool empty;
- lock (queue) {
- job = queue.try_pop();
- assert(job != null);
+
+ queue.lock();
+ job = queue.try_pop_unlocked();
+ assert(job != null);
- assert(enqueued > 0);
- empty = (--enqueued == 0);
- }
+ empty = queue.length_unlocked() == 0;
+ queue.unlock();
if (!job.is_cancelled())
job.execute();
diff --git a/src/util/image.vala b/src/util/image.vala
index 95ac998..5b78a50 100644
--- a/src/util/image.vala
+++ b/src/util/image.vala
@@ -343,7 +343,7 @@ private Cairo.Surface get_background_surface() {
string color_b;
var config = Config.Facade.get_instance();
- var type = "checkered"; //config.get_transparent_background_type();
+ var type = config.get_transparent_background_type();
switch (type) {
case "checkered":
color_a = "#808080";