diff --git a/packaging/targets b/packaging/targets index 9449eb1..63987be 100644 --- a/packaging/targets +++ b/packaging/targets @@ -17,6 +17,7 @@ shell/cpl/desk shell/cpl/printers shell/cpl/sysdm shell/desktop +shell/exitwin shell/explorer shell/run shell/shext/zip diff --git a/shared/comgtk/public/errors.h b/shared/comgtk/public/errors.h index 294d753..38b3ecd 100644 --- a/shared/comgtk/public/errors.h +++ b/shared/comgtk/public/errors.h @@ -36,6 +36,22 @@ void wintc_display_error_and_clear( GtkWindow* wnd ); +/** + * Filters for a particular error code, and propagates it if there is a match. + * + * @param error The error to filter. + * @param domain The error domain. + * @param code The error code. + * @param out_error The error location to propagate to. + * @return True if the error was filtered. + */ +gboolean wintc_filter_error( + GError* error, + GQuark domain, + gint code, + GError** out_error +); + GQuark wintc_general_error_quark(void); /** diff --git a/shared/comgtk/src/errors.c b/shared/comgtk/src/errors.c index 4e4e0fa..81af830 100644 --- a/shared/comgtk/src/errors.c +++ b/shared/comgtk/src/errors.c @@ -32,6 +32,25 @@ void wintc_display_error_and_clear( g_clear_error(error); } +gboolean wintc_filter_error( + GError* error, + GQuark domain, + gint code, + GError** out_error +) +{ + if ( + error->domain == domain && + error->code == code + ) + { + g_propagate_error(out_error, error); + return TRUE; + } + + return FALSE; +} + void wintc_log_error_and_clear( GError** error ) diff --git a/shared/exec/CMakeLists.txt b/shared/exec/CMakeLists.txt index 1b04711..5889eaf 100644 --- a/shared/exec/CMakeLists.txt +++ b/shared/exec/CMakeLists.txt @@ -32,6 +32,8 @@ add_library( src/action.c public/desktop.h src/desktop.c + public/errors.h + src/errors.c public/exec.h src/exec.c public/mime.h diff --git a/shared/exec/public/action.h b/shared/exec/public/action.h index ea6febd..aef0cca 100644 --- a/shared/exec/public/action.h +++ b/shared/exec/public/action.h @@ -57,7 +57,7 @@ typedef enum */ gboolean wintc_launch_action( WinTCAction action_id, - GError** out_error + GError** error ); #endif diff --git a/shared/exec/public/errors.h b/shared/exec/public/errors.h new file mode 100644 index 0000000..73498e2 --- /dev/null +++ b/shared/exec/public/errors.h @@ -0,0 +1,27 @@ +/** @file */ + +#ifndef __EXEC_ERRORS_H__ +#define __EXEC_ERRORS_H__ + +#include + +#define WINTC_EXEC_ERROR wintc_exec_error_quark() + +// +// PUBLIC ENUMS +// + +/** + * Specifies errors relevant to the launch APIs. + */ +typedef enum +{ + WINTC_EXEC_ERROR_FELLTHRU = 1 /** None of the fallbacks were available. */ +} WinTCExecError; + +// +// PUBLIC FUNCTIONS +// +GQuark wintc_exec_error_quark(void); + +#endif diff --git a/shared/exec/public/exec.h b/shared/exec/public/exec.h index d6f6823..055827f 100644 --- a/shared/exec/public/exec.h +++ b/shared/exec/public/exec.h @@ -37,4 +37,18 @@ gboolean wintc_launch_command_sync( GError** error ); +/** + * Attempts to launch command lines until one is successful. + * + * @param error A reference to the GError storage location. + * @param cmdline The first command (highest priority). + * @param ... Subsequent commands that will be tried in sequence. + * @return True if a command was launched successfully. + */ +gboolean wintc_launch_command_with_fallbacks( + GError** error, + const gchar* cmdline, + ... +); + #endif diff --git a/shared/exec/public/libapi.h.in b/shared/exec/public/libapi.h.in index a0e9373..f77fb62 100644 --- a/shared/exec/public/libapi.h.in +++ b/shared/exec/public/libapi.h.in @@ -3,6 +3,7 @@ #include "@LIB_HEADER_DIR@/action.h" #include "@LIB_HEADER_DIR@/desktop.h" +#include "@LIB_HEADER_DIR@/errors.h" #include "@LIB_HEADER_DIR@/exec.h" #include "@LIB_HEADER_DIR@/mime.h" diff --git a/shared/exec/src/action.c b/shared/exec/src/action.c index 6a3528c..7ec7562 100644 --- a/shared/exec/src/action.c +++ b/shared/exec/src/action.c @@ -2,79 +2,66 @@ #include #include "../public/action.h" +#include "../public/errors.h" #include "../public/exec.h" +// +// FORWARD DECLARATIONS +// +gboolean launch_exo_fm( + const gchar* target, + GError** error +); + // // PUBLIC FUNCTIONS // gboolean wintc_launch_action( WinTCAction action_id, - GError** out_error + GError** error ) { - gchar* cmdline; - gboolean ret; - switch (action_id) { case WINTC_ACTION_MYDOCS: - cmdline = - g_strdup_printf( - "exo-open --launch FileManager %s", - g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS) + return + launch_exo_fm( + g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS), + error ); - break; case WINTC_ACTION_MYPICS: - cmdline = - g_strdup_printf( - "exo-open --launch FileManager %s", - g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) + return + launch_exo_fm( + g_get_user_special_dir(G_USER_DIRECTORY_PICTURES), + error ); - break; case WINTC_ACTION_MYMUSIC: - cmdline = - g_strdup_printf( - "exo-open --launch FileManager %s", - g_get_user_special_dir(G_USER_DIRECTORY_MUSIC) + return + launch_exo_fm( + g_get_user_special_dir(G_USER_DIRECTORY_MUSIC), + error ); - break; // TODO: There isn't really a good option for this at the moment, maybe if we // implement an Explorer replica we can have it open a view of disks // case WINTC_ACTION_MYCOMP: - cmdline = - g_strdup_printf( - "%s", - "exo-open --launch FileManager /" + return + launch_exo_fm( + "/", + error ); - break; case WINTC_ACTION_CONTROL: - cmdline = - g_strdup_printf( - "%s", - "xfce4-settings-manager" - ); - break; + return wintc_launch_command("xfce4-settings-manager", error); case WINTC_ACTION_MIMEMGMT: - cmdline = - g_strdup_printf( - "%s", - "xfce4-mime-settings" - ); - break; + return wintc_launch_command("xfce4-mime-settings", error); case WINTC_ACTION_PRINTERS: - cmdline = - g_strdup_printf( - "%s", - "system-config-printer" - ); - break; + return wintc_launch_command("system-config-printer", error); case WINTC_ACTION_RUN: // TODO: In future we will handle missing components gracefully (say, if @@ -82,30 +69,31 @@ gboolean wintc_launch_action( // // See issue #134 for details // - cmdline = - g_strdup_printf( - "%s", - "run" - ); - break; + return wintc_launch_command("run", error); - // TODO: One day write our own log off and shut down dialogs, and execute - // them here - // case WINTC_ACTION_LOGOFF: - case WINTC_ACTION_SHUTDOWN: - cmdline = - g_strdup_printf( - "%s", - "xfce4-session-logout" + return + wintc_launch_command_with_fallbacks( + error, + "wintc-exitwin --user-options", + "xfce4-session-logout", + NULL + ); + + case WINTC_ACTION_SHUTDOWN: + return + wintc_launch_command_with_fallbacks( + error, + "wintc-exitwin --power-options", + "xfce4-session-logout", + NULL ); - break; // Default to 'not implemented' error // default: g_set_error( - out_error, + error, WINTC_GENERAL_ERROR, WINTC_GENERAL_ERROR_NOTIMPL, "Action code is not implemented: %d", @@ -114,8 +102,26 @@ gboolean wintc_launch_action( return FALSE; } +} - ret = wintc_launch_command(cmdline, out_error); +// +// PRIVATE FUNCTIONS +// +gboolean launch_exo_fm( + const gchar* target, + GError** error +) +{ + gchar* cmdline; + gboolean ret; + + cmdline = + g_strdup_printf( + "exo-open --launch FileManager %s", + target + ); + + ret = wintc_launch_command(cmdline, error); g_free(cmdline); diff --git a/shared/exec/src/errors.c b/shared/exec/src/errors.c new file mode 100644 index 0000000..d14f777 --- /dev/null +++ b/shared/exec/src/errors.c @@ -0,0 +1,8 @@ +#include + +#include "../public/errors.h" + +// +// GLIB BOILERPLATE +// +G_DEFINE_QUARK(wintc-exec-error-quark, wintc_exec_error) diff --git a/shared/exec/src/exec.c b/shared/exec/src/exec.c index db08067..0c25164 100644 --- a/shared/exec/src/exec.c +++ b/shared/exec/src/exec.c @@ -6,6 +6,7 @@ #include "../public/desktop.h" #include "../public/mime.h" +#include "../public/errors.h" #include "../public/exec.h" // @@ -101,6 +102,85 @@ gboolean wintc_launch_command_sync( ); } +gboolean wintc_launch_command_with_fallbacks( + GError** error, + const gchar* cmdline, + ... +) +{ + GError* local_error = NULL; + + // Attempt main cmdline + // + if (wintc_launch_command(cmdline, &local_error)) + { + return TRUE; + } + + // Failed, catch all except file not found + // + if ( + wintc_filter_error( + local_error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + error + ) + ) + { + return FALSE; + } + + g_clear_error(&local_error); + + // Failed... continue to try fallbacks one by one until one works + // + va_list ap; + const gchar* next_cmdline; + + va_start(ap, cmdline); + + next_cmdline = va_arg(ap, gchar*); + + while (next_cmdline) + { + if (wintc_launch_command(cmdline, &local_error)) + { + va_end(ap); + return TRUE; + } + + if ( + wintc_filter_error( + local_error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + error + ) + ) + { + return FALSE; + } + + g_clear_error(&local_error); + + next_cmdline = va_arg(ap, gchar*); + } + + va_end(ap); + + // Failed to find any of the fallbacks + // + g_set_error( + error, + WINTC_EXEC_ERROR, + WINTC_EXEC_ERROR_FELLTHRU, + "The program and its alternatives are not present on the system." + ); + + return FALSE; +} + // // PRIVATE FUNCTIONS // diff --git a/shared/msgina/CMakeLists.txt b/shared/msgina/CMakeLists.txt index 6bce99c..6d37ad2 100644 --- a/shared/msgina/CMakeLists.txt +++ b/shared/msgina/CMakeLists.txt @@ -39,11 +39,15 @@ add_library( public/challenge.h src/error.c public/error.h + src/exitwnd.c + public/exitwnd.h src/logon.c public/logon.h public/state.h src/stripctl.c src/stripctl.h + public/xfsm.h + src/xfsm.c ) set_target_properties( diff --git a/shared/msgina/public/exitwnd.h b/shared/msgina/public/exitwnd.h new file mode 100644 index 0000000..ca5bb39 --- /dev/null +++ b/shared/msgina/public/exitwnd.h @@ -0,0 +1,32 @@ +#ifndef __GINA_EXITWND_H__ +#define __GINA_EXITWND_H__ + +#include +#include + +#include "xfsm.h" + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_GINA_EXIT_WINDOW (wintc_gina_exit_window_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCGinaExitWindow, + wintc_gina_exit_window, + WINTC, + GINA_EXIT_WINDOW, + GtkWindow +) + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_exit_window_new_for_power_options( + WinTCGinaSmXfce* sm_xfce +); +GtkWidget* wintc_gina_exit_window_new_for_user_options( + WinTCGinaSmXfce* sm_xfce +); + +#endif diff --git a/shared/msgina/public/libapi.h.in b/shared/msgina/public/libapi.h.in index d9490fc..adbae18 100644 --- a/shared/msgina/public/libapi.h.in +++ b/shared/msgina/public/libapi.h.in @@ -3,7 +3,9 @@ #include "@LIB_HEADER_DIR@/authwnd.h" #include "@LIB_HEADER_DIR@/challenge.h" +#include "@LIB_HEADER_DIR@/exitwnd.h" #include "@LIB_HEADER_DIR@/logon.h" #include "@LIB_HEADER_DIR@/state.h" +#include "@LIB_HEADER_DIR@/xfsm.h" #endif diff --git a/shared/msgina/public/xfsm.h b/shared/msgina/public/xfsm.h new file mode 100644 index 0000000..2fddf77 --- /dev/null +++ b/shared/msgina/public/xfsm.h @@ -0,0 +1,59 @@ +#ifndef __GINA_XFSM_H__ +#define __GINA_XFSM_H__ + +#include + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_GINA_SM_XFCE (wintc_gina_sm_xfce_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCGinaSmXfce, + wintc_gina_sm_xfce, + WINTC, + GINA_SM_XFCE, + GObject // FIXME: one day, implement a session management interface +) + +// +// PUBLIC FUNCTIONS +// +WinTCGinaSmXfce* wintc_gina_sm_xfce_new(void); + +gboolean wintc_gina_sm_xfce_is_valid( + WinTCGinaSmXfce* sm_xfce +); + +gboolean wintc_gina_sm_xfce_can_restart( + WinTCGinaSmXfce* sm_xfce +); +gboolean wintc_gina_sm_xfce_can_shut_down( + WinTCGinaSmXfce* sm_xfce +); +gboolean wintc_gina_sm_xfce_can_sleep( + WinTCGinaSmXfce* sm_xfce +); + +gboolean wintc_gina_sm_xfce_log_off( + WinTCGinaSmXfce* sm_xfce, + GError** error +); +gboolean wintc_gina_sm_xfce_restart( + WinTCGinaSmXfce* sm_xfce, + GError** error +); +gboolean wintc_gina_sm_xfce_shut_down( + WinTCGinaSmXfce* sm_xfce, + GError** error +); +gboolean wintc_gina_sm_xfce_sleep( + WinTCGinaSmXfce* sm_xfce, + GError** error +); +gboolean wintc_gina_sm_xfce_switch_user( + WinTCGinaSmXfce* sm_xfce, + GError** error +); + +#endif diff --git a/shared/msgina/src/exitwnd.c b/shared/msgina/src/exitwnd.c new file mode 100644 index 0000000..8fe38b8 --- /dev/null +++ b/shared/msgina/src/exitwnd.c @@ -0,0 +1,458 @@ +#include +#include +#include +#include + +#include "../public/exitwnd.h" +#include "../public/xfsm.h" + +#define WINTC_GINA_EXITWND_SM_CALL(func) \ + { \ + GError* sm_error = NULL; \ + if (!func(dlg->sm_xfce, &sm_error)) \ + { \ + wintc_display_error_and_clear(&sm_error, GTK_WINDOW(dlg)); \ + return; \ + } \ + gtk_window_close(GTK_WINDOW(dlg)); \ + } + +// +// PRIVATE ENUMS +// +enum // Class properties +{ + PROP_DIALOG_KIND = 1, + PROP_SM, + N_PROPERTIES +}; + +enum +{ + DIALOG_KIND_POWER_OPTIONS, + DIALOG_KIND_USER_OPTIONS +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_gina_exit_window_constructed( + GObject* object +); +static void wintc_gina_exit_window_dispose( + GObject* object +); +static void wintc_gina_exit_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static void on_button_cancel_clicked( + GtkButton* button, + gpointer user_data +); +static void on_button_log_off_clicked( + GtkButton* button, + gpointer user_data +); +static void on_button_restart_clicked( + GtkButton* button, + gpointer user_data +); +static void on_button_stand_by_clicked( + GtkButton* button, + gpointer user_data +); +static void on_button_switch_user_clicked( + GtkButton* button, + gpointer user_data +); +static void on_button_turn_off_clicked( + GtkButton* button, + gpointer user_data +); +static void on_window_destroyed( + GtkWidget* widget, + gpointer user_data +); + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCGinaExitWindow +{ + GtkApplicationWindow __parent__; + + // State + // + gint dialog_kind; + WinTCGinaSmXfce* sm_xfce; +}; + +// +// GTK TYPE DEFINITION & CTORS +// +G_DEFINE_TYPE( + WinTCGinaExitWindow, + wintc_gina_exit_window, + GTK_TYPE_WINDOW +) + +static void wintc_gina_exit_window_class_init( + WinTCGinaExitWindowClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = wintc_gina_exit_window_constructed; + object_class->dispose = wintc_gina_exit_window_dispose; + object_class->set_property = wintc_gina_exit_window_set_property; + + g_object_class_install_property( + object_class, + PROP_DIALOG_KIND, + g_param_spec_int( + "dialog-kind", + "DialogKind", + "The kind of power options dialog to display.", + DIALOG_KIND_POWER_OPTIONS, + DIALOG_KIND_USER_OPTIONS, + DIALOG_KIND_POWER_OPTIONS, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); + g_object_class_install_property( + object_class, + PROP_SM, + g_param_spec_object( + "session-manager", + "SessionManager", + "The session management interface object.", + WINTC_TYPE_GINA_SM_XFCE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); + + // Load up styles + // + GtkCssProvider* css_exitwnd = gtk_css_provider_new(); + + gtk_css_provider_load_from_resource( + css_exitwnd, + "/uk/oddmatics/wintc/msgina/exitwnd.css" + ); + + gtk_style_context_add_provider_for_screen( + gdk_screen_get_default(), + GTK_STYLE_PROVIDER(css_exitwnd), + GTK_STYLE_PROVIDER_PRIORITY_FALLBACK + ); +} + +static void wintc_gina_exit_window_init( + WinTCGinaExitWindow* self +) +{ + gtk_window_set_decorated(GTK_WINDOW(self), FALSE); + gtk_window_set_title(GTK_WINDOW(self), _("Shutdown Windows")); + + wintc_widget_add_style_class(GTK_WIDGET(self), "wintc-exitwnd"); + + g_signal_connect( + GTK_WIDGET(self), + "destroy", + G_CALLBACK(on_window_destroyed), + NULL + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_gina_exit_window_constructed( + GObject* object +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(object); + + GtkBuilder* builder; + GtkWidget* button_restart; + GtkWidget* button_stand_by; + GtkWidget* button_turn_off; + + switch (dlg->dialog_kind) + { + case DIALOG_KIND_POWER_OPTIONS: + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/msgina/pwropts.ui" + ); + + gtk_builder_add_callback_symbols( + builder, + "on_button_restart_clicked", + G_CALLBACK(on_button_restart_clicked), + NULL + ); + gtk_builder_add_callback_symbols( + builder, + "on_button_stand_by_clicked", + G_CALLBACK(on_button_stand_by_clicked), + NULL + ); + gtk_builder_add_callback_symbols( + builder, + "on_button_turn_off_clicked", + G_CALLBACK(on_button_turn_off_clicked), + NULL + ); + + wintc_builder_get_objects( + builder, + "button-restart", &button_restart, + "button-stand-by", &button_stand_by, + "button-turn-off", &button_turn_off, + NULL + ); + + gtk_widget_set_sensitive( + button_restart, + wintc_gina_sm_xfce_can_restart(dlg->sm_xfce) + ); + gtk_widget_set_sensitive( + button_stand_by, + wintc_gina_sm_xfce_can_sleep(dlg->sm_xfce) + ); + gtk_widget_set_sensitive( + button_turn_off, + wintc_gina_sm_xfce_can_shut_down(dlg->sm_xfce) + ); + + break; + + case DIALOG_KIND_USER_OPTIONS: + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/msgina/usropts.ui" + ); + + gtk_builder_add_callback_symbols( + builder, + "on_button_log_off_clicked", + G_CALLBACK(on_button_log_off_clicked), + NULL + ); + gtk_builder_add_callback_symbols( + builder, + "on_button_switch_user_clicked", + G_CALLBACK(on_button_switch_user_clicked), + NULL + ); + + break; + + default: + g_critical( + "msgina: exitwnd unknown dialog kind %d", + dlg->dialog_kind + ); + return; + } + + gtk_builder_add_callback_symbols( + builder, + "on_button_cancel_clicked", + G_CALLBACK(on_button_cancel_clicked), + NULL + ); + + gtk_builder_connect_signals( + builder, + dlg + ); + + // Insert into parent + // + GtkWidget* main_box; + + wintc_builder_get_objects( + builder, + "main-box", &main_box, + NULL + ); + + gtk_container_add(GTK_CONTAINER(dlg), main_box); + + g_object_unref(G_OBJECT(builder)); +} + +static void wintc_gina_exit_window_dispose( + GObject* object +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(object); + + g_clear_object(&(dlg->sm_xfce)); + + (G_OBJECT_CLASS(wintc_gina_exit_window_parent_class))->dispose(object); +} + +static void wintc_gina_exit_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(object); + + switch (prop_id) + { + case PROP_DIALOG_KIND: + dlg->dialog_kind = g_value_get_int(value); + break; + + case PROP_SM: + dlg->sm_xfce = g_value_dup_object(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_exit_window_new_for_power_options( + WinTCGinaSmXfce* sm_xfce +) +{ + GtkWidget* wnd = + GTK_WIDGET( + g_object_new( + WINTC_TYPE_GINA_EXIT_WINDOW, + "dialog-kind", DIALOG_KIND_POWER_OPTIONS, + "session-manager", sm_xfce, + NULL + ) + ); + + GApplication* app = g_application_get_default(); + + if (app && GTK_IS_APPLICATION(app)) + { + gtk_window_set_application( + GTK_WINDOW(wnd), + GTK_APPLICATION(app) + ); + } + + return wnd; +} + +GtkWidget* wintc_gina_exit_window_new_for_user_options( + WinTCGinaSmXfce* sm_xfce +) +{ + GtkWidget* wnd = + GTK_WIDGET( + g_object_new( + WINTC_TYPE_GINA_EXIT_WINDOW, + "dialog-kind", DIALOG_KIND_USER_OPTIONS, + "session-manager", sm_xfce, + NULL + ) + ); + + GApplication* app = g_application_get_default(); + + if (app && GTK_IS_APPLICATION(app)) + { + gtk_window_set_application( + GTK_WINDOW(wnd), + GTK_APPLICATION(app) + ); + } + + return wnd; +} + +// +// CALLBACKS +// +static void on_button_cancel_clicked( + GtkButton* button, + WINTC_UNUSED(gpointer user_data) +) +{ + GtkWidget* toplevel = gtk_widget_get_toplevel(GTK_WIDGET(button)); + + if (GTK_IS_WINDOW(toplevel)) + { + gtk_window_close(GTK_WINDOW(toplevel)); + } +} + +static void on_button_log_off_clicked( + WINTC_UNUSED(GtkButton* button), + gpointer user_data +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(user_data); + + WINTC_GINA_EXITWND_SM_CALL(wintc_gina_sm_xfce_log_off); +} + +static void on_button_restart_clicked( + WINTC_UNUSED(GtkButton* button), + gpointer user_data +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(user_data); + + WINTC_GINA_EXITWND_SM_CALL(wintc_gina_sm_xfce_restart); +} + +static void on_button_stand_by_clicked( + WINTC_UNUSED(GtkButton* button), + gpointer user_data +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(user_data); + + WINTC_GINA_EXITWND_SM_CALL(wintc_gina_sm_xfce_sleep); +} + +static void on_button_switch_user_clicked( + WINTC_UNUSED(GtkButton* button), + gpointer user_data +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(user_data); + + WINTC_GINA_EXITWND_SM_CALL(wintc_gina_sm_xfce_switch_user); +} + +static void on_button_turn_off_clicked( + WINTC_UNUSED(GtkButton* button), + gpointer user_data +) +{ + WinTCGinaExitWindow* dlg = WINTC_GINA_EXIT_WINDOW(user_data); + + WINTC_GINA_EXITWND_SM_CALL(wintc_gina_sm_xfce_shut_down); +} + +static void on_window_destroyed( + GtkWidget* widget, + WINTC_UNUSED(gpointer user_data) +) +{ + GtkApplication* app = gtk_window_get_application(GTK_WINDOW(widget)); + + g_application_quit(G_APPLICATION(app)); +} diff --git a/shared/msgina/src/res/exitwnd.css b/shared/msgina/src/res/exitwnd.css new file mode 100644 index 0000000..bbc9ada --- /dev/null +++ b/shared/msgina/src/res/exitwnd.css @@ -0,0 +1,18 @@ +.wintc-exitwnd +{ + border-radius: 5px; +} + +.wintc-exitwnd label.title +{ + font-size: 14pt; + font-weight: bold; + margin: 4px 10px; +} + +.wintc-exitwnd button +{ + margin: 4px; + min-height: 50px; + min-width: 50px; +} diff --git a/shared/msgina/src/res/pwropts.ui b/shared/msgina/src/res/pwropts.ui new file mode 100644 index 0000000..c2bf9e0 --- /dev/null +++ b/shared/msgina/src/res/pwropts.ui @@ -0,0 +1,149 @@ + + + + + True + False + vertical + + + True + False + + + True + False + Turn off computer + 0.0 + + + + True + True + 0 + + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + + + Stand By + True + True + True + + + + + True + False + 0 + + + + + Turn Off + True + True + True + + + + + True + False + 1 + + + + + Restart + True + True + True + + + + + True + False + 2 + + + + + True + False + 0 + + + + + + True + True + 1 + + + + + True + False + + + Cancel + True + True + True + + + + + False + True + end + 0 + + + + + + False + True + end + 2 + + + + diff --git a/shared/msgina/src/res/resources.xml b/shared/msgina/src/res/resources.xml index 14581cc..5ce353f 100644 --- a/shared/msgina/src/res/resources.xml +++ b/shared/msgina/src/res/resources.xml @@ -1,6 +1,9 @@ - - authwnd.css - - \ No newline at end of file + + authwnd.css + exitwnd.css + pwropts.ui + usropts.ui + + diff --git a/shared/msgina/src/res/usropts.ui b/shared/msgina/src/res/usropts.ui new file mode 100644 index 0000000..c581508 --- /dev/null +++ b/shared/msgina/src/res/usropts.ui @@ -0,0 +1,132 @@ + + + + + + True + False + vertical + + + True + False + + + True + False + Log Off Windows + + + + True + True + 1 + + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + + + Switch User + True + True + True + + + + + True + False + 0 + + + + + Log Off + True + True + True + + + + + True + False + 1 + + + + + True + False + 0 + + + + + + True + True + 1 + + + + + True + False + + + Cancel + True + True + True + + + + + False + True + end + 0 + + + + + + False + True + end + 2 + + + + diff --git a/shared/msgina/src/xfsm.c b/shared/msgina/src/xfsm.c new file mode 100644 index 0000000..25504d8 --- /dev/null +++ b/shared/msgina/src/xfsm.c @@ -0,0 +1,309 @@ +#include +#include + +#include "../public/xfsm.h" + +// +// FORWARD DECLARATIONS +// +static void wintc_gina_sm_xfce_dispose( + GObject* object +); + +static gboolean wintc_gina_sm_xfce_call_can( + WinTCGinaSmXfce* sm_xfce, + const gchar* method_name +); +static gboolean wintc_gina_sm_xfce_call_do( + WinTCGinaSmXfce* sm_xfce, + const gchar* method_name, + GError** error +); + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +typedef struct _WinTCGinaSmXfce +{ + GObject __parent__; + + // State stuff + // + GDBusProxy* proxy; +} WinTCGinaSmXfce; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCGinaSmXfce, + wintc_gina_sm_xfce, + G_TYPE_OBJECT +) + +static void wintc_gina_sm_xfce_class_init( + WinTCGinaSmXfceClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wintc_gina_sm_xfce_dispose; +} + +static void wintc_gina_sm_xfce_init( + WinTCGinaSmXfce* self +) +{ + GError* error = NULL; + + self->proxy = + g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + "org.xfce.SessionManager", + "/org/xfce/SessionManager", + "org.xfce.Session.Manager", + NULL, + &error + ); + + if (!(self->proxy)) + { + wintc_log_error_and_clear(&error); + return; + } +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_gina_sm_xfce_dispose( + GObject* object +) +{ + WinTCGinaSmXfce* sm_xfce = WINTC_GINA_SM_XFCE(object); + + g_clear_object(&(sm_xfce->proxy)); + + (G_OBJECT_CLASS(wintc_gina_sm_xfce_parent_class))->dispose(object); +} + +// +// PUBLIC FUNCTIONS +// +WinTCGinaSmXfce* wintc_gina_sm_xfce_new(void) +{ + return WINTC_GINA_SM_XFCE( + g_object_new( + WINTC_TYPE_GINA_SM_XFCE, + NULL + ) + ); +} + +gboolean wintc_gina_sm_xfce_is_valid( + WinTCGinaSmXfce* sm_xfce +) +{ + return !!(sm_xfce->proxy); +} + +gboolean wintc_gina_sm_xfce_can_restart( + WinTCGinaSmXfce* sm_xfce +) +{ + return wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanRestart" + ); +} + +gboolean wintc_gina_sm_xfce_can_shut_down( + WinTCGinaSmXfce* sm_xfce +) +{ + return wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanShutdown" + ); +} + +gboolean wintc_gina_sm_xfce_can_sleep( + WinTCGinaSmXfce* sm_xfce +) +{ + return wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanHybridSleep" + ); +} + +gboolean wintc_gina_sm_xfce_log_off( + WinTCGinaSmXfce* sm_xfce, + GError** error +) +{ + return wintc_gina_sm_xfce_call_do( + sm_xfce, + "Logout", + error + ); +} + +gboolean wintc_gina_sm_xfce_restart( + WinTCGinaSmXfce* sm_xfce, + GError** error +) +{ + if ( + !wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanRestart" + ) + ) + { + return FALSE; + } + + return wintc_gina_sm_xfce_call_do( + sm_xfce, + "Restart", + error + ); +} + +gboolean wintc_gina_sm_xfce_shut_down( + WinTCGinaSmXfce* sm_xfce, + GError** error +) +{ + if ( + !wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanShutdown" + ) + ) + { + return FALSE; + } + + return wintc_gina_sm_xfce_call_do( + sm_xfce, + "Shutdown", + error + ); +} + +gboolean wintc_gina_sm_xfce_sleep( + WinTCGinaSmXfce* sm_xfce, + GError** error +) +{ + if ( + !wintc_gina_sm_xfce_call_can( + sm_xfce, + "CanHybridSleep" + ) + ) + { + return FALSE; + } + + return wintc_gina_sm_xfce_call_do( + sm_xfce, + "HyrbidSleep", + error + ); +} + +gboolean wintc_gina_sm_xfce_switch_user( + WinTCGinaSmXfce* sm_xfce, + GError** error +) +{ + return wintc_gina_sm_xfce_call_do( + sm_xfce, + "SwitchUser", + error + ); +} + +// +// PRIVATE FUNCTIONS +// +static gboolean wintc_gina_sm_xfce_call_can( + WinTCGinaSmXfce* sm_xfce, + const gchar* method_name +) +{ + if (!(sm_xfce->proxy)) + { + WINTC_LOG_USER_DEBUG( + "gina: attempted to call %s but no connection to xfsm", + method_name + ); + + return FALSE; + } + + GVariant* result_v = + g_dbus_proxy_call_sync( + sm_xfce->proxy, + method_name, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL + ); + + gboolean result; + + g_variant_get( + result_v, + "(b)", + &result + ); + + g_variant_unref(result_v); + + return result; +} + +static gboolean wintc_gina_sm_xfce_call_do( + WinTCGinaSmXfce* sm_xfce, + const gchar* method_name, + GError** error +) +{ + // + // Assume the caller actually bothered to call the CanX method before this + // one + // + GVariant* params_v = NULL; + + if (g_strcmp0(method_name, "Logout") == 0) + { + params_v = g_variant_new("(bb)", FALSE, FALSE); + } + else if ( + g_strcmp0(method_name, "Restart") == 0 || + g_strcmp0(method_name, "Shutdown") == 0 + ) + { + params_v = g_variant_new("(b)", FALSE); + } + + g_dbus_proxy_call_sync( + sm_xfce->proxy, + method_name, + params_v, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error + ); + + return !!error; +} diff --git a/shell/exitwin/CMakeLists.txt b/shell/exitwin/CMakeLists.txt new file mode 100644 index 0000000..a9675f5 --- /dev/null +++ b/shell/exitwin/CMakeLists.txt @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 3.12) + +project( + wintc-exitwin + VERSION 1.0 + DESCRIPTION "Windows Total Conversion Exit Windows program." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/locale/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +include(../../packaging/cmake-inc/resources/CMakeLists.txt) + +wintc_resolve_library(gio-2.0 GIO) +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-msgina WINTC_MSGINA) + +wintc_create_meta_h() + +# Main target +# +add_executable( + wintc-exitwin + src/application.c + src/application.h + src/main.c + src/meta.h +) + +target_compile_options( + wintc-exitwin + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-exitwin + SYSTEM + PRIVATE ${GIO_INCLUDE_DIRS} + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_MSGINA_INCLUDE_DIRS} +) + +target_link_directories( + wintc-exitwin + PRIVATE ${GIO_LIBRARY_DIRS} + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_MSGINA_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-exitwin + PRIVATE ${GIO_LIBRARIES} + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_MSGINA_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() +#wintc_compile_and_install_pofiles() + +install( + TARGETS wintc-exitwin + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/shell/exitwin/README.MD b/shell/exitwin/README.MD new file mode 100644 index 0000000..b79a7ff --- /dev/null +++ b/shell/exitwin/README.MD @@ -0,0 +1,5 @@ +# wintc-exitwin +This directory contains the source code for the exit session program. + +## Note +This program is more like a little stub for spawning the exit session dialog which lives in the `msgina` library. diff --git a/shell/exitwin/deps b/shell/exitwin/deps new file mode 100644 index 0000000..6d2ad3d --- /dev/null +++ b/shell/exitwin/deps @@ -0,0 +1,4 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:wintc-comgtk +bt,rt:wintc-msgina diff --git a/shell/exitwin/src/application.c b/shell/exitwin/src/application.c new file mode 100644 index 0000000..86815e5 --- /dev/null +++ b/shell/exitwin/src/application.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include + +#include "application.h" + +// +// FORWARD DECLARATIONS +// +static void wintc_exitwin_application_activate( + GApplication* application +); + +// +// STATIC DATA +// +static gboolean s_cmd_pwropts = FALSE; +static gboolean s_cmd_usropts = FALSE; + +static const GOptionEntry s_option_entries[] = { + { + "power-options", + 'p', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &s_cmd_pwropts, + N_("Display power options dialog"), + NULL + }, + { + "user-options", + 'u', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &s_cmd_usropts, + N_("Display user options dialog"), + NULL + }, + { 0 } +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCExitwinApplication +{ + GtkApplication __parent__; +}; + +// +// GTK TYPE DEFINITION & CTORS +// +G_DEFINE_TYPE( + WinTCExitwinApplication, + wintc_exitwin_application, + GTK_TYPE_APPLICATION +) + +static void wintc_exitwin_application_class_init( + WinTCExitwinApplicationClass* klass +) +{ + GApplicationClass* application_class = G_APPLICATION_CLASS(klass); + + application_class->activate = wintc_exitwin_application_activate; +} + +static void wintc_exitwin_application_init( + WinTCExitwinApplication* self +) +{ + g_application_add_main_option_entries( + G_APPLICATION(self), + s_option_entries + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_exitwin_application_activate( + WINTC_UNUSED(GApplication* application) +) +{ + static GtkWidget* wnd = NULL; + + if (wnd) + { + return; + } + + // Spawn the session manager interface + // + WinTCGinaSmXfce* sm_xfce = wintc_gina_sm_xfce_new(); + + if (!wintc_gina_sm_xfce_is_valid(sm_xfce)) + { + // FIXME: Localise + // + wintc_messagebox_show( + NULL, + "Failed to connect to the session manager.", + "Error", + GTK_BUTTONS_OK, + GTK_MESSAGE_ERROR + ); + + return; + } + + // All good for spawning the dialog + // + if (s_cmd_usropts) + { + wnd = wintc_gina_exit_window_new_for_user_options(sm_xfce); + } + else + { + wnd = wintc_gina_exit_window_new_for_power_options(sm_xfce); + } + + gtk_widget_show_all(wnd); + + g_object_unref(sm_xfce); // Dialog takes its own reference +} + +// +// PUBLIC FUNCTIONS +// +WinTCExitwinApplication* wintc_exitwin_application_new(void) +{ + WinTCExitwinApplication* app; + + app = + g_object_new( + wintc_exitwin_application_get_type(), + "application-id", "uk.oddmatics.wintc.exitwin", + NULL + ); + + return app; +} diff --git a/shell/exitwin/src/application.h b/shell/exitwin/src/application.h new file mode 100644 index 0000000..81b07bf --- /dev/null +++ b/shell/exitwin/src/application.h @@ -0,0 +1,25 @@ +#ifndef __APPLICATION_H__ +#define __APPLICATION_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_EXITWIN_APPLICATION (wintc_exitwin_application_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCExitwinApplication, + wintc_exitwin_application, + WINTC, + EXITWIN_APPLICATION, + GtkApplication +) + +// +// PUBLIC FUNCTIONS +// +WinTCExitwinApplication* wintc_exitwin_application_new(void); + +#endif diff --git a/shell/exitwin/src/main.c b/shell/exitwin/src/main.c new file mode 100644 index 0000000..9cbb4fe --- /dev/null +++ b/shell/exitwin/src/main.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "application.h" +#include "meta.h" + +int main( + int argc, + char* argv[] +) +{ + // Set up locales + // + setlocale(LC_ALL, ""); + bindtextdomain(PKG_NAME, "/usr/share/locale"); + textdomain(PKG_NAME); + + // Launch application + // + WinTCExitwinApplication* app = wintc_exitwin_application_new(); + int status; + + status = g_application_run(G_APPLICATION(app), argc, argv); + + g_object_unref(app); + + return status; +} diff --git a/shell/taskband/src/start/personal.c b/shell/taskband/src/start/personal.c index d8ada6a..7f63f0d 100644 --- a/shell/taskband/src/start/personal.c +++ b/shell/taskband/src/start/personal.c @@ -970,15 +970,41 @@ static gboolean recent_filter_exclude_directories( } static void on_button_power_clicked( - WINTC_UNUSED(GtkButton* button), - gpointer user_data + GtkButton* button, + gpointer user_data ) { GError* error = NULL; + gtk_widget_hide( + GTK_WIDGET( + wintc_widget_get_toplevel_window(GTK_WIDGET(button)) + ) + ); + if (!wintc_launch_action(GPOINTER_TO_INT(user_data), &error)) { - wintc_nice_error_and_clear(&error, NULL); + if ( + error->domain == WINTC_EXEC_ERROR && + error->code == WINTC_EXEC_ERROR_FELLTHRU + ) + { + g_clear_error(&error); + + // Localise + // + wintc_messagebox_show( + NULL, + "Unable to find any programs for exiting the session.", + "Windows", + GTK_BUTTONS_OK, + GTK_MESSAGE_ERROR + ); + } + else + { + wintc_nice_error_and_clear(&error, NULL); + } } }