diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Application.vala | 109 | ||||
-rw-r--r-- | src/BatchImport.vala | 2 | ||||
-rw-r--r-- | src/CheckerboardLayout.vala | 4 | ||||
-rw-r--r-- | src/DesktopIntegration.vala | 22 | ||||
-rw-r--r-- | src/Resources.vala | 2 | ||||
-rw-r--r-- | src/authenticator.vala | 40 | ||||
-rw-r--r-- | src/direct/DirectWindow.vala | 5 | ||||
-rw-r--r-- | src/folders/FoldersBranch.vala | 2 | ||||
-rw-r--r-- | src/import-roll/ImportRollBranch.vala | 2 | ||||
-rw-r--r-- | src/main.vala | 8 | ||||
-rw-r--r-- | src/meson.build | 14 | ||||
-rw-r--r-- | src/photos/WebPSupport.vala | 6 | ||||
-rw-r--r-- | src/plugins/PublishingInterfaces.vala | 7 | ||||
-rw-r--r-- | src/publishing/PublishingPluginHost.vala | 8 | ||||
-rw-r--r-- | src/threads/Workers.vala | 22 | ||||
-rw-r--r-- | src/util/image.vala | 2 |
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"; |