diff --git a/cx.modal.Reflection.json b/cx.modal.Reflection.json index a32c689..47a3785 100644 --- a/cx.modal.Reflection.json +++ b/cx.modal.Reflection.json @@ -56,9 +56,6 @@ "url" : "./", "branch" : "HEAD" } - ], - "config-opts" : [ - "--libdir=lib" ] } ] diff --git a/meson.build b/meson.build index 0655409..750939d 100644 --- a/meson.build +++ b/meson.build @@ -9,12 +9,16 @@ gnome = import('gnome') application_id = 'cx.modal.Reflection' -pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name() -iconsdir = get_option('datadir') / 'icons' +prefix = get_option('prefix') +bindir = prefix / get_option('bindir') +localedir = prefix / get_option('localedir') -subdir('reflection-app/data') -subdir('reflection-app/src') -subdir('reflection-app/po') +datadir = prefix / get_option('datadir') +pkgdatadir = datadir / meson.project_name() +iconsdir = datadir / 'icons' +podir = meson.project_source_root() / 'po' + +subdir('reflection-app') gnome.post_install( glib_compile_schemas: true, diff --git a/reflection-app/data/cx.modal.Reflection.desktop.in b/reflection-app/data/cx.modal.Reflection.desktop.in deleted file mode 100644 index c7f9357..0000000 --- a/reflection-app/data/cx.modal.Reflection.desktop.in +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Name=reflection -Exec=reflection -Icon=cx.modal.Reflection -Terminal=false -Type=Application -Categories=Utility; -Keywords=GTK; -StartupNotify=true -DBusActivatable=true diff --git a/reflection-app/data/cx.modal.Reflection.desktop.in.in b/reflection-app/data/cx.modal.Reflection.desktop.in.in new file mode 100644 index 0000000..44a7612 --- /dev/null +++ b/reflection-app/data/cx.modal.Reflection.desktop.in.in @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=Reflection +Comment=Collaboratively take meeting notes, even when there's no internet +Exec=reflection +Icon=@icon@ +Terminal=false +Type=Application +Categories=GNOME;GTK;Utility;Network;Office; +# Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! +Keywords=GTK;Adwaita;Modal;p2panda;p2p;peer-to-peer;localfirst;local-first;notes;note-taking;minutes;meeting;multiplayer;multi-player;collaboration;collaborative;real-time;writing;text;editor;pad; +StartupNotify=true +DBusActivatable=true diff --git a/reflection-app/data/cx.modal.Reflection.metainfo.xml.in b/reflection-app/data/cx.modal.Reflection.metainfo.xml.in index a8d1074..3d2c49d 100644 --- a/reflection-app/data/cx.modal.Reflection.metainfo.xml.in +++ b/reflection-app/data/cx.modal.Reflection.metainfo.xml.in @@ -69,8 +69,7 @@ - - https://example.org/changelog.html#version_1.0.1 +

Initial preview release.

    diff --git a/reflection-app/data/cx.modal.Reflection.service.in b/reflection-app/data/cx.modal.Reflection.service.in index 7607299..665a9e5 100644 --- a/reflection-app/data/cx.modal.Reflection.service.in +++ b/reflection-app/data/cx.modal.Reflection.service.in @@ -1,3 +1,3 @@ [D-BUS Service] -Name=cx.modal.Reflection +Name=@application_id@ Exec=@bindir@/reflection --gapplication-service diff --git a/reflection-app/data/meson.build b/reflection-app/data/meson.build index 86a3bd3..9a4151e 100644 --- a/reflection-app/data/meson.build +++ b/reflection-app/data/meson.build @@ -1,10 +1,17 @@ +# Desktop file +desktop_conf = configuration_data() +desktop_conf.set('icon', application_id) desktop_file = i18n.merge_file( - input: 'cx.modal.Reflection.desktop.in', - output: 'cx.modal.Reflection.desktop', - type: 'desktop', - po_dir: '../po', - install: true, - install_dir: get_option('datadir') / 'applications' + type: 'desktop', + input: configure_file( + input: '@0@.desktop.in.in'.format(application_id), + output: '@BASENAME@', + configuration: desktop_conf, + ), + output: '@0@.desktop'.format(application_id), + po_dir: podir, + install: true, + install_dir: datadir / 'applications', ) desktop_utils = find_program('desktop-file-validate', required: false) @@ -13,11 +20,11 @@ if desktop_utils.found() endif appstream_file = i18n.merge_file( - input: 'cx.modal.Reflection.metainfo.xml.in', - output: 'cx.modal.Reflection.metainfo.xml', - po_dir: '../po', - install: true, - install_dir: get_option('datadir') / 'metainfo' + input: 'cx.modal.Reflection.metainfo.xml.in', + output: '@0@.metainfo.xml'.format(application_id), + po_dir: podir, + install: true, + install_dir: datadir / 'metainfo', ) appstreamcli = find_program('appstreamcli', required: false, disabler: true) @@ -34,13 +41,16 @@ test('Validate schema file', args: ['--strict', '--dry-run', meson.current_source_dir()]) +# D-Bus service file service_conf = configuration_data() -service_conf.set('bindir', get_option('prefix') / get_option('bindir')) +service_conf.set('application_id', application_id) +service_conf.set('bindir', bindir) configure_file( - input: 'cx.modal.Reflection.service.in', - output: 'cx.modal.Reflection.service', - configuration: service_conf, - install_dir: get_option('datadir') / 'dbus-1' / 'services' + input: 'cx.modal.Reflection.service.in', + output: '@0@.service'.format(application_id), + configuration: service_conf, + install: true, + install_dir: datadir / 'dbus-1/services', ) subdir('icons') diff --git a/reflection-app/meson.build b/reflection-app/meson.build new file mode 100644 index 0000000..e9fb4d1 --- /dev/null +++ b/reflection-app/meson.build @@ -0,0 +1,3 @@ +subdir('data') +subdir('src') +subdir('po') \ No newline at end of file diff --git a/reflection-app/src/application.rs b/reflection-app/src/application.rs index 6dfcc4d..c4c9592 100644 --- a/reflection-app/src/application.rs +++ b/reflection-app/src/application.rs @@ -52,6 +52,7 @@ mod imp { #[property(get, nullable)] pub service: RefCell>, pub startup_error: RefCell>, + pub service_startup_task: RefCell>>, #[property(get)] pub system_settings: SystemSettings, } @@ -77,36 +78,19 @@ mod imp { impl ApplicationImpl for ReflectionApplication { fn startup(&self) { - let service: Result = - glib::MainContext::default().block_on(async move { - let private_key = secret::get_or_create_identity().await?; - - let mut data_path = glib::user_data_dir(); - data_path.push("Reflection"); - data_path.push(private_key.public_key().to_string()); - fs::create_dir_all(&data_path)?; - let data_dir = gio::File::for_path(data_path); - - let service = Service::new(&private_key, Some(&data_dir)); - service.startup().await?; - Ok(service) - }); - - match service { - Ok(service) => { - self.service.replace(Some(service)); - } - Err(error) => { - error!("Failed to start service: {error}"); - self.startup_error.replace(Some(error)); - } - } - self.parent_startup(); + + gtk::Window::set_default_icon_name(config::APP_ID); } fn shutdown(&self) { glib::MainContext::default().block_on(async move { + // Make sure service startup finished + if let Some(handle) = self.service_startup_task.take() { + handle + .await + .expect("Service startup to complete on shutdown"); + } if let Some(service) = self.obj().service() { service.shutdown().await; } @@ -115,6 +99,8 @@ mod imp { } fn activate(&self) { + self.parent_activate(); + self.obj().new_window(); } } @@ -130,10 +116,10 @@ glib::wrapper! { } impl ReflectionApplication { - pub fn new(application_id: &str, flags: &gio::ApplicationFlags) -> Self { + pub fn new() -> Self { glib::Object::builder() - .property("application-id", application_id) - .property("flags", flags) + .property("application-id", config::APP_ID) + .property("flags", gio::ApplicationFlags::empty()) .build() } @@ -271,12 +257,62 @@ impl ReflectionApplication { ]); } + async fn create_service(&self) -> Result { + let private_key = secret::get_or_create_identity().await?; + + let mut data_path = glib::user_data_dir(); + data_path.push("Reflection"); + data_path.push(private_key.public_key().to_string()); + fs::create_dir_all(&data_path)?; + let data_dir = gio::File::for_path(data_path); + + let service = Service::new(&private_key, Some(&data_dir)); + service.startup().await?; + + Ok(service) + } + fn new_window(&self) -> Window { let window = Window::new(self); - window.set_service(self.service()); + if let Some(error) = self.imp().startup_error.borrow().as_ref() { window.display_startup_error(error); } + + if let Some(service) = self.service() { + window.set_service(Some(service)); + } else if self.imp().service_startup_task.borrow().is_none() { + let handle = glib::spawn_future_local(clone!( + #[weak(rename_to = obj)] + self, + async move { + let service = obj.create_service().await; + + match service { + Ok(service) => { + for window in obj.windows() { + if let Ok(window) = window.downcast::() { + window.set_service(Some(&service)); + } + } + obj.imp().service.replace(Some(service)); + } + Err(error) => { + error!("Failed to start service: {error}"); + for window in obj.windows() { + if let Ok(window) = window.downcast::() { + window.display_startup_error(&error); + } + } + obj.imp().startup_error.replace(Some(error)); + } + } + } + )); + + self.imp().service_startup_task.replace(Some(handle)); + } + window.present(); window } diff --git a/reflection-app/src/main.rs b/reflection-app/src/main.rs index 33fd89c..05751da 100644 --- a/reflection-app/src/main.rs +++ b/reflection-app/src/main.rs @@ -57,25 +57,19 @@ fn main() -> glib::ExitCode { .expect("Unable to set the text domain encoding"); textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain"); - // Load resources let res = gio::Resource::load(RESOURCES_FILE).expect("Could not load gresource file"); gio::resources_register(&res); let ui_res = gio::Resource::load(UI_RESOURCES_FILE).expect("Could not load UI gresource file"); gio::resources_register(&ui_res); - // Create a new GtkApplication. The application manages our main loop, - // application windows, integration with the window manager/compositor, and - // desktop features such as file opening and single-instance applications. - let app = ReflectionApplication::new("cx.modal.Reflection", &gio::ApplicationFlags::empty()); + gtk::glib::set_application_name("Reflection"); + gtk::init().expect("Could not start GTK4"); info!("Reflection ({})", APP_ID); info!("Version: {}", VERSION); info!("Datadir: {}", PKGDATADIR); - // Run the application. This function will block until the application - // exits. Upon return, we have our exit code to return to the shell. (This - // is the code you see when you do `echo $?` after running a command in a - // terminal. + let app = ReflectionApplication::new(); app.run() } diff --git a/reflection-app/src/meson.build b/reflection-app/src/meson.build index 29cb0c2..3b51cbc 100644 --- a/reflection-app/src/meson.build +++ b/reflection-app/src/meson.build @@ -1,5 +1,3 @@ -gnome = import('gnome') - blueprints = custom_target('blueprints', input: files( 'shortcuts-dialog.blp' @@ -20,10 +18,10 @@ conf = configuration_data() conf.set_quoted('APP_ID', application_id) conf.set_quoted('VERSION', meson.project_version()) conf.set_quoted('GETTEXT_PACKAGE', 'reflection') -conf.set_quoted('LOCALEDIR', get_option('prefix') / get_option('localedir')) +conf.set_quoted('LOCALEDIR', localedir) conf.set_quoted('PKGDATADIR', pkgdatadir) -configure_file( +config_file = configure_file( input: 'config.rs.in', output: 'config.rs', configuration: conf @@ -32,7 +30,7 @@ configure_file( # Copy the config.rs output to the source directory. run_command( 'cp', - meson.project_build_root() / 'reflection-app/src' / 'config.rs', + config_file, meson.project_source_root() / 'reflection-app/src' / 'config.rs', check: true ) @@ -56,7 +54,7 @@ cargo_build = custom_target( output: meson.project_name(), console: true, install: true, - install_dir: get_option('bindir'), + install_dir: bindir, command: [ 'env', cargo_env, cargo_bin, 'build',