Enhancement: Fixes #178, Scaffold XP Power Dialog

This commit is contained in:
Rory Fewell
2025-06-06 21:51:05 +01:00
parent cfa26d9137
commit 5aa075f95c
28 changed files with 1725 additions and 67 deletions

View File

@@ -17,6 +17,7 @@ shell/cpl/desk
shell/cpl/printers
shell/cpl/sysdm
shell/desktop
shell/exitwin
shell/explorer
shell/run
shell/shext/zip

View File

@@ -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);
/**

View File

@@ -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
)

View File

@@ -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

View File

@@ -57,7 +57,7 @@ typedef enum
*/
gboolean wintc_launch_action(
WinTCAction action_id,
GError** out_error
GError** error
);
#endif

View File

@@ -0,0 +1,27 @@
/** @file */
#ifndef __EXEC_ERRORS_H__
#define __EXEC_ERRORS_H__
#include <glib.h>
#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

View File

@@ -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

View File

@@ -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"

View File

@@ -2,79 +2,66 @@
#include <wintc/comgtk.h>
#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);

8
shared/exec/src/errors.c Normal file
View File

@@ -0,0 +1,8 @@
#include <glib.h>
#include "../public/errors.h"
//
// GLIB BOILERPLATE
//
G_DEFINE_QUARK(wintc-exec-error-quark, wintc_exec_error)

View File

@@ -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
//

View File

@@ -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(

View File

@@ -0,0 +1,32 @@
#ifndef __GINA_EXITWND_H__
#define __GINA_EXITWND_H__
#include <glib.h>
#include <gtk/gtk.h>
#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

View File

@@ -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

View File

@@ -0,0 +1,59 @@
#ifndef __GINA_XFSM_H__
#define __GINA_XFSM_H__
#include <glib.h>
//
// 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

458
shared/msgina/src/exitwnd.c Normal file
View File

@@ -0,0 +1,458 @@
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <wintc/comgtk.h>
#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));
}

View File

@@ -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;
}

View File

@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="main-box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Turn off computer</property>
<property name="xalign">0.0</property>
<style>
<class name="title"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="title"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="button-stand-by">
<property name="label" translatable="yes">Stand By</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_stand_by_clicked" swapped="no"/>
<style>
<class name="standby"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button-turn-off">
<property name="label" translatable="yes">Turn Off</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_turn_off_clicked" swapped="no"/>
<style>
<class name="turn-off"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button-restart">
<property name="label" translatable="yes">Restart</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_restart_clicked" swapped="no"/>
<style>
<class name="restart"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="pwropts"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="button-cancel">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_cancel_clicked" swapped="no"/>
<style>
<class name="cancel"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="cancel" />
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</interface>

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/uk/oddmatics/wintc/msgina">
<file>authwnd.css</file>
</gresource>
</gresources>
<gresource prefix="/uk/oddmatics/wintc/msgina">
<file>authwnd.css</file>
<file>exitwnd.css</file>
<file>pwropts.ui</file>
<file>usropts.ui</file>
</gresource>
</gresources>

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="main-box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Log Off Windows</property>
<style>
<class name="title"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="title"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="button-switch-user">
<property name="label" translatable="yes">Switch User</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_switch_user_clicked" swapped="no"/>
<style>
<class name="switch-user"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button-log-off">
<property name="label" translatable="yes">Log Off</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_log_off_clicked" swapped="no"/>
<style>
<class name="log-off"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="pwropts"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="button-cancel">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_button_cancel_clicked" swapped="no"/>
<style>
<class name="cancel"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<style>
<class name="cancel" />
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</interface>

309
shared/msgina/src/xfsm.c Normal file
View File

@@ -0,0 +1,309 @@
#include <glib.h>
#include <wintc/comgtk.h>
#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;
}

View File

@@ -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 <roryf@oddmatics.uk>")
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}
)

5
shell/exitwin/README.MD Normal file
View File

@@ -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.

4
shell/exitwin/deps Normal file
View File

@@ -0,0 +1,4 @@
bt,rt:glib2
bt,rt:gtk3
bt,rt:wintc-comgtk
bt,rt:wintc-msgina

View File

@@ -0,0 +1,145 @@
#include <gio/gio.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <wintc/comgtk.h>
#include <wintc/msgina.h>
#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;
}

View File

@@ -0,0 +1,25 @@
#ifndef __APPLICATION_H__
#define __APPLICATION_H__
#include <glib.h>
#include <gtk/gtk.h>
//
// 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

30
shell/exitwin/src/main.c Normal file
View File

@@ -0,0 +1,30 @@
#include <gio/gio.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <locale.h>
#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;
}

View File

@@ -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);
}
}
}