diff --git a/base/logonui/CMakeLists.txt b/base/logonui/CMakeLists.txt new file mode 100644 index 0000000..85bf124 --- /dev/null +++ b/base/logonui/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.0) + +project( + wintc-logonui + VERSION 1.0 + DESCRIPTION "Windows Total Conversion logon user interface." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS false) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +# Necessary because we use g_signal_handlers_disconnect... +set(WINTC_NO_PEDANTIC_COMPILE true) + +include(GNUInstallDirs) + +include(../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +include(../../packaging/cmake-inc/resources/CMakeLists.txt) + +wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF) +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(liblightdm-gobject-1 LIGHTDM) +wintc_resolve_library(wintc-comctl WINTC_COMCTL) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-msgina WINTC_MSGINA) + +wintc_compile_resources() + +add_executable( + wintc-logonui + src/main.c + src/resources.c + src/window.c + src/window.h + src/classic/ui.c + src/classic/ui.h + src/welcome/ui.c + src/welcome/ui.h + src/welcome/userlist.c + src/welcome/userlist.h +) + +set_target_properties( + wintc-logonui + PROPERTIES + OUTPUT_NAME logonui +) + +target_compile_options( + wintc-logonui + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-logonui + SYSTEM + PRIVATE ${GDK_PIXBUF_INCLUDE_DIRS} + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${LIGHTDM_INCLUDE_DIRS} + PRIVATE ${WINTC_COMCTL_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_MSGINA_INCLUDE_DIRS} +) + +target_link_directories( + wintc-logonui + PRIVATE ${GDK_PIXBUF_LIBRARY_DIRS} + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${LIGHTDM_LIBRARY_DIRS} + PRIVATE ${WINTC_COMCTL_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_MSGINA_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-logonui + PRIVATE ${GDK_PIXBUF_LIBRARIES} + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${LIGHTDM_LIBRARIES} + PRIVATE ${WINTC_COMCTL_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_MSGINA_LIBRARIES} +) + +add_dependencies( + wintc-logonui + build-gresources +) + +# Installation +# +wintc_configure_and_install_packaging() + +install( + FILES wintc-logonui.desktop + DESTINATION share/xgreeters +) +install( + TARGETS wintc-logonui + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) diff --git a/base/logonui/README.MD b/base/logonui/README.MD new file mode 100644 index 0000000..5daaa89 --- /dev/null +++ b/base/logonui/README.MD @@ -0,0 +1,4 @@ +# logonui +This directory contains the source-code for the Windows Logon User Interface. + +![image](https://github.com/rozniak/xfce-winxp-tc/assets/13258281/83f8c2e4-3e03-478f-b7e2-8721ee43f71a) diff --git a/base/logonui/deps b/base/logonui/deps new file mode 100644 index 0000000..ea016cb --- /dev/null +++ b/base/logonui/deps @@ -0,0 +1,7 @@ +bt,rt:gdk-pixbuf2 +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:lightdm +bt,rt:wintc-comctl +bt,rt:wintc-comgtk +bt,rt:wintc-msgina diff --git a/base/logonui/src/classic/ui.c b/base/logonui/src/classic/ui.c new file mode 100644 index 0000000..c980b69 --- /dev/null +++ b/base/logonui/src/classic/ui.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +#include "ui.h" +#include "../window.h" + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCClassicUIPrivate +{ + GtkWidget* wnd_gina; +}; + +struct _WinTCClassicUIClass +{ + GtkWidgetClass __parent__; +}; + +struct _WinTCClassicUI +{ + GtkWidget __parent__; + + WinTCClassicUIPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static gboolean wintc_classic_ui_draw( + GtkWidget* widget, + cairo_t* cr +); + +static void on_self_realized( + GtkWidget* self, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCClassicUI, + wintc_classic_ui, + GTK_TYPE_WIDGET, + G_ADD_PRIVATE(WinTCClassicUI) +) + +static void wintc_classic_ui_class_init( + WinTCClassicUIClass* klass +) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + + widget_class->draw = wintc_classic_ui_draw; +} + +static void wintc_classic_ui_init( + WinTCClassicUI* self +) +{ + self->priv = wintc_classic_ui_get_instance_private(self); + + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); + + // Set up widgets + // + self->priv->wnd_gina = wintc_gina_auth_window_new(); + + // Connect to realize signal to begin when we're ready + // + g_signal_connect( + self, + "realize", + G_CALLBACK(on_self_realized), + NULL + ); +} + +// +// CLASS VIRTUAL METHODS +// +static gboolean wintc_classic_ui_draw( + WINTC_UNUSED(GtkWidget* widget), + cairo_t* cr +) +{ + // BG color is #004E98 + // FIXME: This is the default desktop colour, should be it be defined in + // a shell lib? Also see shell/desktop + // + cairo_set_source_rgb(cr, 0.0f, 0.298f, 0.596f); + cairo_paint(cr); + + return FALSE; +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_classic_ui_new(void) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_CLASSIC_UI, + "hexpand", TRUE, + "vexpand", TRUE, + NULL + ) + ); +} + +// +// CALLBACKS +// +static void on_self_realized( + GtkWidget* self, + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCClassicUI* classic_ui = WINTC_CLASSIC_UI(self); + + gtk_widget_show_all(classic_ui->priv->wnd_gina); +} \ No newline at end of file diff --git a/base/logonui/src/classic/ui.h b/base/logonui/src/classic/ui.h new file mode 100644 index 0000000..6c8d925 --- /dev/null +++ b/base/logonui/src/classic/ui.h @@ -0,0 +1,28 @@ +#ifndef __CLASSIC_UI_H__ +#define __CLASSIC_UI_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCClassicUIPrivate WinTCClassicUIPrivate; +typedef struct _WinTCClassicUIClass WinTCClassicUIClass; +typedef struct _WinTCClassicUI WinTCClassicUI; + +#define TYPE_WINTC_CLASSIC_UI (wintc_classic_ui_get_type()) +#define WINTC_CLASSIC_UI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_CLASSIC_UI, WinTCClassicUI)) +#define WINTC_CLASSIC_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((obj), TYPE_WINTC_CLASSIC_UI, WinTCClassicUI)) +#define IS_WINTC_CLASSIC_UI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_CLASSIC_UI)) +#define IS_WINTC_CLASSIC_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_CLASSIC_UI)) +#define WINTC_CLASSIC_UI_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_CLASSIC_UI)) + +GType wintc_classic_ui_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_classic_ui_new(void); + +#endif diff --git a/base/logonui/src/main.c b/base/logonui/src/main.c new file mode 100644 index 0000000..040f24b --- /dev/null +++ b/base/logonui/src/main.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "window.h" + +// +// ENTRY POINT +// +int main( + int argc, + char* argv[] +) +{ + GtkWidget* window; + + gtk_init(&argc, &argv); + + // Set up theme + // FIXME: There should probably be an API for this rather than directly + // plopping the name straight in GTK + // FIXME: This should be configurable via INI option and simply default to + // Luna for clients, and Classic for servers + // + g_object_set( + gtk_settings_get_default(), + "gtk-theme-name", + "Windows XP style (Blue)", + NULL + ); + + // Create the window and launch + // + window = wintc_logonui_window_new(); + + gtk_widget_show_all(window); + + gtk_main(); + + return 0; +} diff --git a/base/logonui/src/res/bglight.png b/base/logonui/src/res/bglight.png new file mode 100644 index 0000000..dd9bd44 Binary files /dev/null and b/base/logonui/src/res/bglight.png differ diff --git a/base/logonui/src/res/ejctbtn.png b/base/logonui/src/res/ejctbtn.png new file mode 100644 index 0000000..ba57f76 Binary files /dev/null and b/base/logonui/src/res/ejctbtn.png differ diff --git a/base/logonui/src/res/ejctbtna.png b/base/logonui/src/res/ejctbtna.png new file mode 100644 index 0000000..1025a01 Binary files /dev/null and b/base/logonui/src/res/ejctbtna.png differ diff --git a/base/logonui/src/res/entry.png b/base/logonui/src/res/entry.png new file mode 100644 index 0000000..7eaa998 Binary files /dev/null and b/base/logonui/src/res/entry.png differ diff --git a/base/logonui/src/res/gobtn.png b/base/logonui/src/res/gobtn.png new file mode 100644 index 0000000..23c443b Binary files /dev/null and b/base/logonui/src/res/gobtn.png differ diff --git a/base/logonui/src/res/gobtna.png b/base/logonui/src/res/gobtna.png new file mode 100644 index 0000000..3283657 Binary files /dev/null and b/base/logonui/src/res/gobtna.png differ diff --git a/base/logonui/src/res/hintbtn.png b/base/logonui/src/res/hintbtn.png new file mode 100644 index 0000000..b4710ff Binary files /dev/null and b/base/logonui/src/res/hintbtn.png differ diff --git a/base/logonui/src/res/hintbtna.png b/base/logonui/src/res/hintbtna.png new file mode 100644 index 0000000..470364c Binary files /dev/null and b/base/logonui/src/res/hintbtna.png differ diff --git a/base/logonui/src/res/hsepa.png b/base/logonui/src/res/hsepa.png new file mode 100644 index 0000000..3538b4a Binary files /dev/null and b/base/logonui/src/res/hsepa.png differ diff --git a/base/logonui/src/res/hsepb.png b/base/logonui/src/res/hsepb.png new file mode 100644 index 0000000..ad19c1a Binary files /dev/null and b/base/logonui/src/res/hsepb.png differ diff --git a/base/logonui/src/res/logo.png b/base/logonui/src/res/logo.png new file mode 100644 index 0000000..0eb135f Binary files /dev/null and b/base/logonui/src/res/logo.png differ diff --git a/base/logonui/src/res/logoani.png b/base/logonui/src/res/logoani.png new file mode 100644 index 0000000..17db7e9 Binary files /dev/null and b/base/logonui/src/res/logoani.png differ diff --git a/base/logonui/src/res/resources.xml b/base/logonui/src/res/resources.xml new file mode 100644 index 0000000..781fd3e --- /dev/null +++ b/base/logonui/src/res/resources.xml @@ -0,0 +1,35 @@ + + + + + + + bglight.png + ejctbtna.png + ejctbtn.png + entry.png + gobtna.png + gobtn.png + hintbtna.png + hintbtn.png + hsepa.png + hsepb.png + logoani.png + logo.png + scdnbtn.png + scrollbar.png + scupbtn.png + shtdbtna.png + shtdbtn.png + tilehot.png + tile.png + userpic.png + usersel.png + vsep.png + + + + + welcome-ui.css + + diff --git a/base/logonui/src/res/scdnbtn.png b/base/logonui/src/res/scdnbtn.png new file mode 100644 index 0000000..1dd3932 Binary files /dev/null and b/base/logonui/src/res/scdnbtn.png differ diff --git a/base/logonui/src/res/scrollbar.png b/base/logonui/src/res/scrollbar.png new file mode 100644 index 0000000..1e55020 Binary files /dev/null and b/base/logonui/src/res/scrollbar.png differ diff --git a/base/logonui/src/res/scupbtn.png b/base/logonui/src/res/scupbtn.png new file mode 100644 index 0000000..bc61b9b Binary files /dev/null and b/base/logonui/src/res/scupbtn.png differ diff --git a/base/logonui/src/res/shtdbtn.png b/base/logonui/src/res/shtdbtn.png new file mode 100644 index 0000000..9002582 Binary files /dev/null and b/base/logonui/src/res/shtdbtn.png differ diff --git a/base/logonui/src/res/shtdbtna.png b/base/logonui/src/res/shtdbtna.png new file mode 100644 index 0000000..78f9711 Binary files /dev/null and b/base/logonui/src/res/shtdbtna.png differ diff --git a/base/logonui/src/res/tile.png b/base/logonui/src/res/tile.png new file mode 100644 index 0000000..6f5846d Binary files /dev/null and b/base/logonui/src/res/tile.png differ diff --git a/base/logonui/src/res/tilehot.png b/base/logonui/src/res/tilehot.png new file mode 100644 index 0000000..c6211a7 Binary files /dev/null and b/base/logonui/src/res/tilehot.png differ diff --git a/base/logonui/src/res/userpic.png b/base/logonui/src/res/userpic.png new file mode 100644 index 0000000..ccc06f0 Binary files /dev/null and b/base/logonui/src/res/userpic.png differ diff --git a/base/logonui/src/res/usersel.png b/base/logonui/src/res/usersel.png new file mode 100644 index 0000000..74bdda9 Binary files /dev/null and b/base/logonui/src/res/usersel.png differ diff --git a/base/logonui/src/res/vsep.png b/base/logonui/src/res/vsep.png new file mode 100644 index 0000000..ad46cf0 Binary files /dev/null and b/base/logonui/src/res/vsep.png differ diff --git a/base/logonui/src/res/welcome-ui.css b/base/logonui/src/res/welcome-ui.css new file mode 100644 index 0000000..fb5b88c --- /dev/null +++ b/base/logonui/src/res/welcome-ui.css @@ -0,0 +1,21 @@ +.box-instruction label, +.box-wait label, +.box-welcome label +{ + color: #EFF7FF; + font-family: 'Arial'; +} + +.box-instruction label, +.box-wait label +{ + font-size: 14pt; +} + +.box-welcome label +{ + font-size: 36pt; + font-style: italic; + font-weight: bold; + text-shadow: 2px 3px 0px #3151B5; +} \ No newline at end of file diff --git a/base/logonui/src/welcome/ui.c b/base/logonui/src/welcome/ui.c new file mode 100644 index 0000000..26ca3c0 --- /dev/null +++ b/base/logonui/src/welcome/ui.c @@ -0,0 +1,846 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "ui.h" +#include "userlist.h" +#include "../window.h" + +#define DELAY_SECONDS_AT_LEAST 2 +#define DELAY_SECONDS_POLL 1 + +#define LOGOANI_FRAME_COUNT 50 +#define LOGOANI_FRAME_RATE 15 + +#define TOP_RIBBON_THICKNESS 78 +#define BOTTOM_RIBBON_THICKNESS 94 +#define STRIP_THICKNESS 2 + +// HMARGIN is potentially 40 or thereabouts +// WIDTH is based off 360 per half + 1 for vseparator +// VMARGIN is a guesstimate +// +#define INNER_BOX_HMARGIN 32 +#define INNER_BOX_WIDTH (720 + 1) +#define INNER_BOX_VMARGIN 8 +#define INNER_BOX_COLLAPSE ((INNER_BOX_HMARGIN * 2) + INNER_BOX_WIDTH) + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCWelcomeUIPrivate +{ + GSList* child_widgets; + + // Graphic resources + // + GdkPixbuf* pixbuf_bglight; + GdkPixbuf* pixbuf_hsepa; + GdkPixbuf* pixbuf_hsepb; + cairo_surface_t* surface_bglight; + cairo_surface_t* surface_hsepa; + cairo_surface_t* surface_hsepb; + + // Logo resources + // + guint ani_id_logo_ins; + guint ani_id_logo_wait; + GdkPixbuf* pixbuf_logo; + GdkPixbuf* pixbuf_logoani; + + // UI + // + GtkWidget* box_container; + + GtkWidget* box_instruction; + GtkWidget* box_login; + GtkWidget* box_wait; + GtkWidget* box_welcome; + + GtkWidget* animation_logo_ins; + GtkWidget* animation_logo_wait; + GtkWidget* label_instruction; + GtkWidget* label_wait; + GtkWidget* label_welcome; + GtkWidget* scrollwnd; + GtkWidget* user_list; + + // State + // + WinTCGinaState current_state; + WinTCGinaLogonSession* logon_session; +}; + +struct _WinTCWelcomeUIClass +{ + GtkContainerClass __parent__; + + GtkCssProvider* css_provider; +}; + +struct _WinTCWelcomeUI +{ + GtkContainer __parent__; + + WinTCWelcomeUIPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_welcome_ui_finalize( + GObject* gobject +); + +static gboolean wintc_welcome_ui_draw( + GtkWidget* widget, + cairo_t* cr +); +static void wintc_welcome_ui_size_allocate( + GtkWidget* widget, + GtkAllocation* allocation +); + +static void wintc_welcome_ui_add( + GtkContainer* container, + GtkWidget* widget +); +static void wintc_welcome_ui_forall( + GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data +); +static void wintc_welcome_ui_remove( + GtkContainer* container, + GtkWidget* widget +); + +static void wintc_welcome_ui_change_state( + WinTCWelcomeUI* welcome_ui, + WinTCGinaState next_state +); +static void wintc_welcome_ui_internal_add( + WinTCWelcomeUI* welcome_ui, + GtkWidget* widget +); + +static void on_self_realized( + GtkWidget* self, + gpointer user_data +); + +static void on_logon_session_attempt_complete( + WinTCGinaLogonSession* logon_session, + WinTCGinaResponse response, + gpointer user_data +); + +static gboolean on_timeout_delay_done( + gpointer user_data +); +static gboolean on_timeout_poll_ready( + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCWelcomeUI, + wintc_welcome_ui, + GTK_TYPE_CONTAINER, + G_ADD_PRIVATE(WinTCWelcomeUI) +) + +static void wintc_welcome_ui_class_init( + WinTCWelcomeUIClass* klass +) +{ + GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass); + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_welcome_ui_finalize; + + widget_class->draw = wintc_welcome_ui_draw; + widget_class->size_allocate = wintc_welcome_ui_size_allocate; + + container_class->add = wintc_welcome_ui_add; + container_class->forall = wintc_welcome_ui_forall; + container_class->remove = wintc_welcome_ui_remove; + + klass->css_provider = gtk_css_provider_new(); + + gtk_css_provider_load_from_resource( + klass->css_provider, + "/uk/oddmatics/wintc/logonui/welcome-ui.css" + ); + + gtk_style_context_add_provider_for_screen( + gdk_screen_get_default(), + GTK_STYLE_PROVIDER(klass->css_provider), + GTK_STYLE_PROVIDER_PRIORITY_FALLBACK + ); +} + +static void wintc_welcome_ui_init( + WinTCWelcomeUI* self +) +{ + self->priv = wintc_welcome_ui_get_instance_private(self); + + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); + + // Set initial state + // + self->priv->current_state = WINTC_GINA_STATE_NONE; + self->priv->logon_session = wintc_gina_logon_session_new(); + + g_signal_connect( + self->priv->logon_session, + "attempt-complete", + G_CALLBACK(on_logon_session_attempt_complete), + self + ); + + // Set up image resources + // + self->priv->pixbuf_bglight = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/bglight.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_hsepa = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/hsepa.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_hsepb = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/hsepb.png", + NULL // FIXME: Error reporting + ); + + self->priv->surface_bglight = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_bglight, + 1, + NULL + ); + self->priv->surface_hsepa = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_hsepa, + 1, + NULL + ); + self->priv->surface_hsepb = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_hsepb, + 1, + NULL + ); + + self->priv->pixbuf_logo = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/logo.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_logoani = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/logoani.png", + NULL // FIXME: Error reporting + ); + + // Set up 'Please wait...' box + // + self->priv->box_wait = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + self->priv->animation_logo_wait = wintc_animation_new(); + self->priv->label_wait = gtk_label_new("Please wait..."); + + self->priv->ani_id_logo_wait = + wintc_animation_add_static( + WINTC_ANIMATION(self->priv->animation_logo_wait), + self->priv->pixbuf_logo + ); + wintc_animation_set_halign( + WINTC_ANIMATION(self->priv->animation_logo_wait), + GTK_ALIGN_END + ); + wintc_animation_set_valign( + WINTC_ANIMATION(self->priv->animation_logo_wait), + GTK_ALIGN_END + ); + wintc_animation_play( + WINTC_ANIMATION(self->priv->animation_logo_wait), + self->priv->ani_id_logo_wait, + 0, + WINTC_ANIMATION_INFINITE + ); + + gtk_label_set_xalign(GTK_LABEL(self->priv->label_wait), 1.0f); + gtk_label_set_yalign(GTK_LABEL(self->priv->label_wait), 0.0f); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_wait), + self->priv->animation_logo_wait, + TRUE, + TRUE, + 0 + ); + gtk_box_pack_start( + GTK_BOX(self->priv->box_wait), + self->priv->label_wait, + TRUE, + TRUE, + 0 + ); + + // Set up instruction box + // + self->priv->box_instruction = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + self->priv->animation_logo_ins = wintc_animation_new(); + self->priv->label_instruction = + gtk_label_new("To begin, click your user name"); + + gtk_label_set_xalign(GTK_LABEL(self->priv->label_instruction), 1.0f); + gtk_label_set_yalign(GTK_LABEL(self->priv->label_instruction), 0.0f); + + self->priv->ani_id_logo_ins = + wintc_animation_add_framesheet( + WINTC_ANIMATION(self->priv->animation_logo_ins), + self->priv->pixbuf_logoani, + LOGOANI_FRAME_COUNT + ); + wintc_animation_set_halign( + WINTC_ANIMATION(self->priv->animation_logo_ins), + GTK_ALIGN_END + ); + wintc_animation_set_valign( + WINTC_ANIMATION(self->priv->animation_logo_ins), + GTK_ALIGN_END + ); + wintc_animation_play( + WINTC_ANIMATION(self->priv->animation_logo_ins), + self->priv->ani_id_logo_ins, + LOGOANI_FRAME_RATE, + WINTC_ANIMATION_INFINITE + ); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_instruction), + self->priv->animation_logo_ins, + TRUE, + TRUE, + 0 + ); + gtk_box_pack_start( + GTK_BOX(self->priv->box_instruction), + self->priv->label_instruction, + TRUE, + TRUE, + 0 + ); + + // Set up login box + // + self->priv->box_login = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + self->priv->scrollwnd = gtk_scrolled_window_new(NULL, NULL); + self->priv->user_list = + wintc_welcome_user_list_new(self->priv->logon_session); + + gtk_container_add( + GTK_CONTAINER(self->priv->scrollwnd), + self->priv->user_list + ); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_login), + self->priv->scrollwnd, + FALSE, + FALSE, + 0 + ); + gtk_box_set_center_widget( + GTK_BOX(self->priv->box_login), + self->priv->scrollwnd + ); + + // Set up 'welcome' box + // + self->priv->box_welcome = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + self->priv->label_welcome = gtk_label_new("welcome"); + + gtk_label_set_xalign(GTK_LABEL(self->priv->label_welcome), 0.8f); + gtk_label_set_yalign(GTK_LABEL(self->priv->label_welcome), 0.5f); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_welcome), + self->priv->label_welcome, + TRUE, + TRUE, + 0 + ); + + // Set up container + // + self->priv->box_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + + gtk_box_set_homogeneous( + GTK_BOX(self->priv->box_container), + TRUE + ); + + wintc_welcome_ui_internal_add(self, self->priv->box_container); + + // Add style classes + // + wintc_widget_add_style_class(self->priv->box_instruction, "box-instruction"); + wintc_widget_add_style_class(self->priv->box_login, "box-login"); + wintc_widget_add_style_class(self->priv->box_wait, "box-wait"); + wintc_widget_add_style_class(self->priv->box_welcome, "box-welcome"); + + // Hold an additional reference to the boxes, so we can add/remove + // them ourselves without them getting binned + // + g_object_ref(self->priv->box_instruction); + g_object_ref(self->priv->box_login); + g_object_ref(self->priv->box_wait); + g_object_ref(self->priv->box_welcome); + + // Connect to realize signal to kick off everything when we're + // actually live + // + g_signal_connect( + self, + "realize", + G_CALLBACK(on_self_realized), + NULL + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_welcome_ui_finalize( + GObject* gobject +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(gobject); + + // Bin graphical resources + // + cairo_surface_destroy(welcome_ui->priv->surface_bglight); + cairo_surface_destroy(welcome_ui->priv->surface_hsepa); + cairo_surface_destroy(welcome_ui->priv->surface_hsepb); + g_clear_object(&(welcome_ui->priv->pixbuf_bglight)); + g_clear_object(&(welcome_ui->priv->pixbuf_hsepa)); + g_clear_object(&(welcome_ui->priv->pixbuf_hsepb)); + g_clear_object(&(welcome_ui->priv->pixbuf_logoani)); + + // Bin additional references held for the boxes + // + g_clear_object(&(welcome_ui->priv->box_instruction)); + g_clear_object(&(welcome_ui->priv->box_login)); + g_clear_object(&(welcome_ui->priv->box_wait)); + g_clear_object(&(welcome_ui->priv->box_welcome)); + + (G_OBJECT_CLASS(wintc_welcome_ui_parent_class))->finalize(gobject); +} + +static gboolean wintc_welcome_ui_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(widget); + + gint height = gtk_widget_get_allocated_height(widget); + gint width = gtk_widget_get_allocated_width(widget); + + // Background is #5A7EDC + // + cairo_set_source_rgb(cr, 0.35f, 0.49f, 0.86f); + cairo_paint(cr); + + // Draw top banner edge + // + gint hsepa_width = gdk_pixbuf_get_width(welcome_ui->priv->pixbuf_hsepa); + + cairo_save(cr); + + cairo_scale(cr, (double) width / (double) hsepa_width, 1.0f); + cairo_set_source_surface( + cr, + welcome_ui->priv->surface_hsepa, + 0.0f, + (double) TOP_RIBBON_THICKNESS + ); + cairo_paint(cr); + + cairo_restore(cr); + + // Draw top banner fill (#00309C) + // + cairo_save(cr); + + cairo_rectangle( + cr, + 0.0f, + 0.0f, + (double) width, + (double) TOP_RIBBON_THICKNESS + ); + cairo_clip(cr); + cairo_set_source_rgb(cr, 0.0f, 0.19f, 0.61f); + cairo_paint(cr); + + cairo_restore(cr); + + // Draw bg light + // + cairo_set_source_surface( + cr, + welcome_ui->priv->surface_bglight, + 0.0f, + (double) (TOP_RIBBON_THICKNESS + STRIP_THICKNESS) + ); + cairo_paint(cr); + + // Draw bottom banner edge + // + gint hsepb_width = gdk_pixbuf_get_width(welcome_ui->priv->pixbuf_hsepb); + + cairo_save(cr); + + cairo_scale(cr, (double) width / (double) hsepb_width, 1.0f); + cairo_set_source_surface( + cr, + welcome_ui->priv->surface_hsepb, + 0.0f, + (double) (height - BOTTOM_RIBBON_THICKNESS - STRIP_THICKNESS) + ); + cairo_paint(cr); + + cairo_restore(cr); + + // Draw bottom banner fill (#1D32A4) + // FIXME: This should actually be a gradient! + // + cairo_save(cr); + + cairo_rectangle( + cr, + 0.0f, + (double) (height - BOTTOM_RIBBON_THICKNESS), + (double) width, + (double) BOTTOM_RIBBON_THICKNESS + ); + cairo_clip(cr); + cairo_set_source_rgb(cr, 0.11f, 0.19f, 0.64f); + cairo_paint(cr); + + cairo_restore(cr); + + // Chain up + // + (GTK_WIDGET_CLASS(wintc_welcome_ui_parent_class))->draw(widget, cr); + + return FALSE; +} + +static void wintc_welcome_ui_size_allocate( + GtkWidget* widget, + GtkAllocation* allocation +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(widget); + + gtk_widget_set_allocation(widget, allocation); + + // FIXME: We're not handling collapse at low res (640x480) + // FIXME: We're also not doing anything about low res on the Y axis + // + if (allocation->width < INNER_BOX_COLLAPSE) + { + g_critical( + "Screen res is too low, 800x600 required and we got width of %d", + allocation->width + ); + return; + } + + // Just deal with >= 800x600 for now + // + gint centre_ui = allocation->width / 2; + gint centre_box = (INNER_BOX_WIDTH / 2); + + GtkAllocation box_alloc; + + box_alloc.x = centre_ui - centre_box + INNER_BOX_HMARGIN; + box_alloc.y = TOP_RIBBON_THICKNESS + + STRIP_THICKNESS + + INNER_BOX_VMARGIN; + box_alloc.width = INNER_BOX_WIDTH; + box_alloc.height = allocation->height + - TOP_RIBBON_THICKNESS + - BOTTOM_RIBBON_THICKNESS + - (STRIP_THICKNESS * 2) + - (INNER_BOX_VMARGIN * 2); + + gtk_widget_size_allocate( + welcome_ui->priv->box_container, + &box_alloc + ); +} + +static void wintc_welcome_ui_add( + WINTC_UNUSED(GtkContainer* container), + WINTC_UNUSED(GtkWidget* widget) +) +{ + g_critical("%s", "wintc_welcome_ui_add - not allowed!"); +} + +static void wintc_welcome_ui_forall( + GtkContainer* container, + WINTC_UNUSED(gboolean include_internals), + GtkCallback callback, + gpointer callback_data +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(container); + + g_slist_foreach( + welcome_ui->priv->child_widgets, + (GFunc) callback, + callback_data + ); +} + +static void wintc_welcome_ui_remove( + WINTC_UNUSED(GtkContainer* container), + WINTC_UNUSED(GtkWidget* widget) +) +{ + g_critical("%s", "wintc_welcome_ui_remove - not allowed!"); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_welcome_ui_new(void) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_WELCOME_UI, + "hexpand", TRUE, + "vexpand", TRUE, + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_welcome_ui_change_state( + WinTCWelcomeUI* welcome_ui, + WinTCGinaState next_state +) +{ + // Disable current state, if any + // + switch (welcome_ui->priv->current_state) + { + case WINTC_GINA_STATE_STARTING: + gtk_container_remove( + GTK_CONTAINER(welcome_ui->priv->box_container), + welcome_ui->priv->box_wait + ); + break; + + case WINTC_GINA_STATE_PROMPT: + gtk_container_remove( + GTK_CONTAINER(welcome_ui->priv->box_container), + welcome_ui->priv->box_instruction + ); + gtk_container_remove( + GTK_CONTAINER(welcome_ui->priv->box_container), + welcome_ui->priv->box_login + ); + break; + + case WINTC_GINA_STATE_LAUNCHING: + gtk_container_remove( + GTK_CONTAINER(welcome_ui->priv->box_container), + welcome_ui->priv->box_welcome + ); + break; + + default: break; + } + + // Set up new state + // + switch (next_state) + { + case WINTC_GINA_STATE_STARTING: + gtk_box_pack_start( + GTK_BOX(welcome_ui->priv->box_container), + welcome_ui->priv->box_wait, + TRUE, + TRUE, + 0 + ); + wintc_gina_logon_session_establish( + welcome_ui->priv->logon_session + ); + g_timeout_add_seconds( + DELAY_SECONDS_AT_LEAST, + on_timeout_delay_done, + welcome_ui + ); + break; + + case WINTC_GINA_STATE_PROMPT: + gtk_box_pack_start( + GTK_BOX(welcome_ui->priv->box_container), + welcome_ui->priv->box_instruction, + TRUE, + TRUE, + 0 + ); + gtk_box_pack_start( + GTK_BOX(welcome_ui->priv->box_container), + welcome_ui->priv->box_login, + TRUE, + TRUE, + 0 + ); + break; + + case WINTC_GINA_STATE_LAUNCHING: + gtk_box_pack_start( + GTK_BOX(welcome_ui->priv->box_container), + welcome_ui->priv->box_welcome, + TRUE, + TRUE, + 0 + ); + g_timeout_add_seconds( + DELAY_SECONDS_AT_LEAST, + on_timeout_delay_done, + welcome_ui + ); + break; + + default: break; + } + + gtk_widget_show_all( + welcome_ui->priv->box_container + ); + + welcome_ui->priv->current_state = next_state; +} + +static void wintc_welcome_ui_internal_add( + WinTCWelcomeUI* welcome_ui, + GtkWidget* widget +) +{ + gtk_widget_set_parent(widget, GTK_WIDGET(welcome_ui)); + + welcome_ui->priv->child_widgets = + g_slist_append(welcome_ui->priv->child_widgets, widget); +} + +// +// CALLBACKS +// +static void on_self_realized( + GtkWidget* self, + WINTC_UNUSED(gpointer user_data) +) +{ + wintc_welcome_ui_change_state( + WINTC_WELCOME_UI(self), + WINTC_GINA_STATE_STARTING + ); +} + +static void on_logon_session_attempt_complete( + WINTC_UNUSED(WinTCGinaLogonSession* logon_session), + WinTCGinaResponse response, + gpointer user_data +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(user_data); + + if (response == WINTC_GINA_RESPONSE_OKAY) + { + wintc_welcome_ui_change_state( + welcome_ui, + WINTC_GINA_STATE_LAUNCHING + ); + } +} + +static gboolean on_timeout_delay_done( + gpointer user_data +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(user_data); + + switch (welcome_ui->priv->current_state) + { + case WINTC_GINA_STATE_STARTING: + g_timeout_add_seconds( + DELAY_SECONDS_POLL, + on_timeout_poll_ready, + welcome_ui + ); + break; + + case WINTC_GINA_STATE_LAUNCHING: + wintc_gina_logon_session_finish( + welcome_ui->priv->logon_session + ); + break; + + default: + g_critical("%s", "Invalid state reached for delay."); + break; + } + + return G_SOURCE_REMOVE; +} + +static gboolean on_timeout_poll_ready( + gpointer user_data +) +{ + WinTCWelcomeUI* welcome_ui = WINTC_WELCOME_UI(user_data); + + if ( + wintc_gina_logon_session_is_available( + welcome_ui->priv->logon_session + ) + ) + { + wintc_welcome_ui_change_state( + welcome_ui, + WINTC_GINA_STATE_PROMPT + ); + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} \ No newline at end of file diff --git a/base/logonui/src/welcome/ui.h b/base/logonui/src/welcome/ui.h new file mode 100644 index 0000000..e6bec95 --- /dev/null +++ b/base/logonui/src/welcome/ui.h @@ -0,0 +1,28 @@ +#ifndef __WELCOME_UI_H__ +#define __WELCOME_UI_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCWelcomeUIPrivate WinTCWelcomeUIPrivate; +typedef struct _WinTCWelcomeUIClass WinTCWelcomeUIClass; +typedef struct _WinTCWelcomeUI WinTCWelcomeUI; + +#define TYPE_WINTC_WELCOME_UI (wintc_welcome_ui_get_type()) +#define WINTC_WELCOME_UI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_WELCOME_UI, WinTCWelcomeUI)) +#define WINTC_WELCOME_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_WELCOME_UI, WinTCWelcomeUI)) +#define IS_WINTC_WELCOME_UI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_WELCOME_UI)) +#define IS_WINTC_WELCOME_UI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_WELCOME_UI)) +#define WINTC_WELCOME_UI_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_WELCOME_UI)) + +GType wintc_welcome_ui_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_welcome_ui_new(void); + +#endif diff --git a/base/logonui/src/welcome/userlist.c b/base/logonui/src/welcome/userlist.c new file mode 100644 index 0000000..0456fde --- /dev/null +++ b/base/logonui/src/welcome/userlist.c @@ -0,0 +1,1084 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "userlist.h" + +#define DELAY_SECONDS_POLL 1 + +// Defining offsets +// +#define USER_LISTING_WIDTH 318 + +#define USER_LISTING_GAP_Y_HALF 5 + +#define USER_TILE_OFFSET_X 7 +#define USER_TILE_OFFSET_Y 6 + +#define USER_PIC_OFFSET 5 + +#define USER_NAME_OFFSET_X 75 +#define USER_NAME_OFFSET_Y 25 + +// +// PRIVATE ENUMS +// +enum +{ + PROP_LOGON_SESSION = 1, + PROP_HADJUSTMENT, + PROP_VADJUSTMENT, + PROP_HSCROLL_POLICY, + PROP_VSCROLL_POLICY, + N_PROPERTIES +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCWelcomeUserListPrivate +{ + GdkWindow* hwnd; + + GSList* child_widgets; + + GtkWidget* box_auth; + GtkWidget* button_go; + GtkWidget* entry_password; + + WinTCGinaLogonSession* logon_session; + + // Control stuff + // + gint selected_index; + GList* users; + + // Graphic resources + // + GdkPixbuf* pixbuf_tile; + GdkPixbuf* pixbuf_tilehot; + GdkPixbuf* pixbuf_userpic; + GdkPixbuf* pixbuf_usersel; + cairo_surface_t* surface_tile; + cairo_surface_t* surface_tilehot; + cairo_surface_t* surface_userpic; + cairo_surface_t* surface_usersel; + + // Geometry + gint item_height; + + GtkAdjustment* hadjustment; + GtkAdjustment* vadjustment; + + gint hscroll_policy; + gint vscroll_policy; + + gint scroll_x; + gint scroll_y; +}; + +struct _WinTCWelcomeUserListClass +{ + GtkContainerClass __parent__; +}; + +struct _WinTCWelcomeUserList +{ + GtkContainer __parent__; + + WinTCWelcomeUserListPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_welcome_user_list_finalize( + GObject* gobject +); +static void wintc_welcome_user_list_get_property( + GObject* gobject, + guint prop_id, + GValue* value, + GParamSpec* pspec +); +static void wintc_welcome_user_list_set_property( + GObject* gobject, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static gboolean wintc_welcome_user_list_button_press_event( + GtkWidget* widget, + GdkEventButton* event +); +static gboolean wintc_welcome_user_list_draw( + GtkWidget* widget, + cairo_t* cr +); +static void wintc_welcome_user_list_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +); +static void wintc_welcome_user_list_get_preferred_height_for_width( + GtkWidget* widget, + gint width, + gint* minimum_height, + gint* natural_height +); +static void wintc_welcome_user_list_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +); +static void wintc_welcome_user_list_get_preferred_width_for_height( + GtkWidget* widget, + gint height, + gint* minimum_width, + gint* natural_width +); +static void wintc_welcome_user_list_realize( + GtkWidget* widget +); +static void wintc_welcome_user_list_size_allocate( + GtkWidget* widget, + GtkAllocation* allocation +); + +static void wintc_welcome_user_list_add( + GtkContainer* container, + GtkWidget* widget +); +static void wintc_welcome_user_list_forall( + GtkContainer* container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data +); +static void wintc_welcome_user_list_remove( + GtkContainer* container, + GtkWidget* widget +); + +static void wintc_welcome_user_list_internal_add( + WinTCWelcomeUserList* user_list, + GtkWidget* widget +); +static void wintc_welcome_user_list_select_user( + WinTCWelcomeUserList* user_list, + gint index +); +static void wintc_welcome_user_list_set_vadjustment( + WinTCWelcomeUserList* user_list, + GtkAdjustment* adjustment +); +static void wintc_welcome_user_list_set_vadjustment_values( + WinTCWelcomeUserList* user_list +); + +static void draw_user( + cairo_t* cr, + LightDMUser* user, + const gboolean selected, + const WinTCWelcomeUserList* user_list +); + +static void on_self_adjustment_changed( + GtkAdjustment* adjustment, + WinTCWelcomeUserList* user_list +); + +static void on_logon_session_attempt_complete( + WinTCGinaLogonSession* logon_session, + WinTCGinaResponse response, + gpointer user_data +); + +static void on_button_go_clicked( + GtkButton* self, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCWelcomeUserList, + wintc_welcome_user_list, + GTK_TYPE_CONTAINER, + G_ADD_PRIVATE(WinTCWelcomeUserList) + G_IMPLEMENT_INTERFACE( + GTK_TYPE_SCROLLABLE, + NULL + ) +) + +static void wintc_welcome_user_list_class_init( + WinTCWelcomeUserListClass* klass +) +{ + GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass); + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_welcome_user_list_finalize; + object_class->get_property = wintc_welcome_user_list_get_property; + object_class->set_property = wintc_welcome_user_list_set_property; + + widget_class->button_press_event = + wintc_welcome_user_list_button_press_event; + widget_class->draw = wintc_welcome_user_list_draw; + widget_class->get_preferred_height = + wintc_welcome_user_list_get_preferred_height; + widget_class->get_preferred_height_for_width = + wintc_welcome_user_list_get_preferred_height_for_width; + widget_class->get_preferred_width = + wintc_welcome_user_list_get_preferred_width; + widget_class->get_preferred_width_for_height = + wintc_welcome_user_list_get_preferred_width_for_height; + widget_class->realize = wintc_welcome_user_list_realize; + widget_class->size_allocate = wintc_welcome_user_list_size_allocate; + + container_class->add = wintc_welcome_user_list_add; + container_class->forall = wintc_welcome_user_list_forall; + container_class->remove = wintc_welcome_user_list_remove; + + g_object_class_install_property( + object_class, + PROP_LOGON_SESSION, + g_param_spec_object( + "logon-session", + "LogonSession", + "The GINA logon session instance.", + TYPE_WINTC_GINA_LOGON_SESSION, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); + + // Scrollable interface properties + // + g_object_class_override_property( + object_class, + PROP_HADJUSTMENT, + "hadjustment" + ); + g_object_class_override_property( + object_class, + PROP_VADJUSTMENT, + "vadjustment" + ); + g_object_class_override_property( + object_class, + PROP_HSCROLL_POLICY, + "hscroll-policy" + ); + g_object_class_override_property( + object_class, + PROP_VSCROLL_POLICY, + "vscroll-policy" + ); +} + +static void wintc_welcome_user_list_init( + WinTCWelcomeUserList* self +) +{ + self->priv = wintc_welcome_user_list_get_instance_private(self); + + gtk_widget_set_has_window(GTK_WIDGET(self), TRUE); + + // Set up image resources + // + self->priv->pixbuf_tile = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/tile.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_tilehot = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/tilehot.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_userpic = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/userpic.png", + NULL // FIXME: Error reporting + ); + self->priv->pixbuf_usersel = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/logonui/usersel.png", + NULL // FIXME: Error reporting + ); + + self->priv->surface_tile = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_tile, + 1, + NULL + ); + self->priv->surface_tilehot = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_tilehot, + 1, + NULL + ); + self->priv->surface_userpic = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_userpic, + 1, + NULL + ); + self->priv->surface_usersel = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_usersel, + 1, + NULL + ); + + // Set up widgets + // + self->priv->box_auth = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + self->priv->button_go = gtk_button_new_with_label("Go"); + self->priv->entry_password = gtk_entry_new(); + + gtk_entry_set_visibility( + GTK_ENTRY(self->priv->entry_password), + FALSE + ); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_auth), + self->priv->entry_password, + FALSE, + FALSE, + 0 + ); + gtk_box_pack_start( + GTK_BOX(self->priv->box_auth), + self->priv->button_go, + FALSE, + FALSE, + 0 + ); + + wintc_welcome_user_list_internal_add( + self, + self->priv->box_auth + ); + + gtk_widget_set_sensitive( + self->priv->entry_password, + FALSE + ); + + g_signal_connect( + self->priv->button_go, + "clicked", + G_CALLBACK(on_button_go_clicked), + self + ); + + // Add style classes + // + wintc_widget_add_style_class(self->priv->button_go, "go"); + wintc_widget_add_style_class(self->priv->entry_password, "password"); + + // Store item height + // + gint usersel_height = gdk_pixbuf_get_height(self->priv->pixbuf_usersel); + + self->priv->item_height = usersel_height + (2 * USER_LISTING_GAP_Y_HALF); + + // Set up default scroll policy + // + self->priv->hscroll_policy = GTK_SCROLL_MINIMUM; + self->priv->vscroll_policy = GTK_SCROLL_NATURAL; + + // Retrieve users + // + self->priv->users = + lightdm_user_list_get_users( + lightdm_user_list_get_instance() + ); + + self->priv->selected_index = -1; +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_welcome_user_list_finalize( + GObject* gobject +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(gobject); + + cairo_surface_destroy(user_list->priv->surface_tile); + cairo_surface_destroy(user_list->priv->surface_tilehot); + cairo_surface_destroy(user_list->priv->surface_userpic); + cairo_surface_destroy(user_list->priv->surface_usersel); + g_clear_object(&user_list->priv->pixbuf_tile); + g_clear_object(&user_list->priv->pixbuf_tilehot); + g_clear_object(&user_list->priv->pixbuf_userpic); + g_clear_object(&user_list->priv->pixbuf_usersel); + + (G_OBJECT_CLASS(wintc_welcome_user_list_parent_class))->finalize(gobject); +} + +static void wintc_welcome_user_list_get_property( + GObject* gobject, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(gobject); + + switch (prop_id) + { + case PROP_HADJUSTMENT: + g_value_set_object(value, user_list->priv->hadjustment); + break; + + case PROP_VADJUSTMENT: + g_value_set_object(value, user_list->priv->vadjustment); + break; + + case PROP_HSCROLL_POLICY: + g_value_set_enum(value, user_list->priv->hscroll_policy); + break; + + case PROP_VSCROLL_POLICY: + g_value_set_enum(value, user_list->priv->vscroll_policy); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + break; + } +} + +static void wintc_welcome_user_list_set_property( + GObject* gobject, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(gobject); + + switch (prop_id) + { + case PROP_LOGON_SESSION: + user_list->priv->logon_session = + WINTC_GINA_LOGON_SESSION(g_value_get_object(value)); + + g_signal_connect( + user_list->priv->logon_session, + "attempt-complete", + G_CALLBACK(on_logon_session_attempt_complete), + user_list + ); + + break; + + // Dummy out HADJUSTMENT - not supporting horz scrolling + // + case PROP_HADJUSTMENT: return; + + case PROP_VADJUSTMENT: + wintc_welcome_user_list_set_vadjustment( + user_list, + g_value_get_object(value) + ); + break; + + case PROP_HSCROLL_POLICY: + if (user_list->priv->hscroll_policy != g_value_get_enum(value)) + { + user_list->priv->hscroll_policy = g_value_get_enum(value); + gtk_widget_queue_resize(GTK_WIDGET(user_list)); + g_object_notify_by_pspec(gobject, pspec); + } + break; + + case PROP_VSCROLL_POLICY: + if (user_list->priv->vscroll_policy != g_value_get_enum(value)) + { + user_list->priv->vscroll_policy = g_value_get_enum(value); + gtk_widget_queue_resize(GTK_WIDGET(user_list)); + g_object_notify_by_pspec(gobject, pspec); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec); + break; + } +} + +static gboolean wintc_welcome_user_list_button_press_event( + GtkWidget* widget, + GdkEventButton* event +) +{ + gint hit_item; + gdouble scroll_y; + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(widget); + + scroll_y = gtk_adjustment_get_value(user_list->priv->vadjustment); + hit_item = (gint) (scroll_y + event->y) / user_list->priv->item_height; + + if (hit_item >= (gint) g_list_length(user_list->priv->users)) + { + hit_item = -1; + } + + wintc_welcome_user_list_select_user(user_list, hit_item); + + return TRUE; +} + +static gboolean wintc_welcome_user_list_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(widget); + + // Acquire adjustment + // + gdouble scroll_y = gtk_adjustment_get_value(user_list->priv->vadjustment); + + // Acquire clip region + // + gdouble clip_bottom; + gdouble clip_left; + gdouble clip_right; + gdouble clip_top; + + cairo_clip_extents(cr, &clip_left, &clip_top, &clip_right, &clip_bottom); + + // Check what items we need to redraw + // + gdouble region_bottom = clip_bottom + scroll_y; + gdouble region_top = clip_top + scroll_y; + + gint item_first = (gint) region_top / user_list->priv->item_height; + gint item_last = (gint) region_bottom / user_list->priv->item_height; + gint items = g_list_length(user_list->priv->users); + gdouble width = clip_right - clip_left; + + item_first = item_first < 0 ? 0 : item_first; + + for (int i = item_first; i < items && i <= item_last; i++) + { + gdouble item_y = (double) (i * user_list->priv->item_height) - scroll_y; + + cairo_save(cr); + + cairo_rectangle( + cr, + 0.0f, + item_y, + width, + (double) user_list->priv->item_height + ); + cairo_clip(cr); + + // FIXME: Why on earth do we need this? + cairo_move_to(cr, 0.0f, item_y); + + draw_user( + cr, + (g_list_nth(user_list->priv->users, i))->data, + user_list->priv->selected_index == i, + user_list + ); + + cairo_restore(cr); + } + + // Chain up + // + (GTK_WIDGET_CLASS(wintc_welcome_user_list_parent_class)) + ->draw(widget, cr); + + return FALSE; +} + +static void wintc_welcome_user_list_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(widget); + + *minimum_height = user_list->priv->item_height; + *natural_height = user_list->priv->item_height * + g_list_length(user_list->priv->users); +} + +static void wintc_welcome_user_list_get_preferred_height_for_width( + GtkWidget* widget, + WINTC_UNUSED(gint width), + gint* minimum_height, + gint* natural_height +) +{ + wintc_welcome_user_list_get_preferred_height( + widget, + minimum_height, + natural_height + ); +} + +static void wintc_welcome_user_list_get_preferred_width( + WINTC_UNUSED(GtkWidget* widget), + gint* minimum_width, + gint* natural_width +) +{ + *minimum_width = USER_LISTING_WIDTH; + *natural_width = USER_LISTING_WIDTH; +} + +static void wintc_welcome_user_list_get_preferred_width_for_height( + GtkWidget* widget, + WINTC_UNUSED(gint height), + gint* minimum_width, + gint* natural_width +) +{ + wintc_welcome_user_list_get_preferred_width( + widget, + minimum_width, + natural_width + ); +} + +static void wintc_welcome_user_list_realize( + GtkWidget* widget +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(widget); + + // Initial widget stuffs + // + GtkAllocation allocation; + + gtk_widget_get_allocation(widget, &allocation); + + gtk_widget_set_realized(widget, TRUE); + + // Create input window + // + GdkWindowAttr attribs; + + attribs.x = allocation.x; + attribs.y = allocation.y; + attribs.width = allocation.width; + attribs.height = allocation.height; + attribs.event_mask = GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK; + attribs.wclass = GDK_INPUT_OUTPUT; + attribs.visual = gtk_widget_get_visual(widget); + attribs.window_type = GDK_WINDOW_CHILD; + + user_list->priv->hwnd = + gdk_window_new( + gtk_widget_get_parent_window(widget), + &attribs, + GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL + ); + + gtk_widget_set_window( + widget, + user_list->priv->hwnd + ); + gtk_widget_register_window( + widget, + user_list->priv->hwnd + ); +} + +static void wintc_welcome_user_list_size_allocate( + GtkWidget* widget, + GtkAllocation* allocation +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(widget); + + gtk_widget_set_allocation(widget, allocation); + + // Update GDK window positioning + // + if (gtk_widget_get_realized(widget)) + { + gdk_window_move_resize( + user_list->priv->hwnd, + allocation->x, + allocation->y, + allocation->width, + allocation->height + ); + } + + // Position auth widget + // + GtkAllocation box_alloc; + + if (user_list->priv->selected_index > -1) + { + gint scroll_y = + gtk_adjustment_get_value( + user_list->priv->vadjustment + ); + + box_alloc.x = 70; + box_alloc.y = user_list->priv->selected_index * + user_list->priv->item_height + + 50 - scroll_y; + box_alloc.width = 248; + box_alloc.height = 30; + + gtk_widget_show_all(user_list->priv->box_auth); + } + else + { + box_alloc.x = 0; + box_alloc.y = 0; + box_alloc.width = 0; + box_alloc.height = 0; + + gtk_widget_hide(user_list->priv->box_auth); + } + + gtk_widget_size_allocate( + user_list->priv->box_auth, + &box_alloc + ); + + // Update scroll values + // + wintc_welcome_user_list_set_vadjustment_values(user_list); +} + +static void wintc_welcome_user_list_add( + WINTC_UNUSED(GtkContainer* container), + WINTC_UNUSED(GtkWidget* widget) +) +{ + g_critical("%s", "wintc_welcome_user_list_add - not allowed!"); +} + +static void wintc_welcome_user_list_forall( + GtkContainer* container, + WINTC_UNUSED(gboolean include_internals), + GtkCallback callback, + gpointer callback_data +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(container); + + g_slist_foreach( + user_list->priv->child_widgets, + (GFunc) callback, + callback_data + ); +} + +static void wintc_welcome_user_list_remove( + WINTC_UNUSED(GtkContainer* container), + WINTC_UNUSED(GtkWidget* widget) +) +{ + g_critical("%s", "wintc_welcome_user_list_remove - not allowed!"); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_welcome_user_list_new( + WinTCGinaLogonSession* logon_session +) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_WELCOME_USER_LIST, + "logon-session", logon_session, + "hexpand", TRUE, + "vexpand", TRUE, + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_welcome_user_list_internal_add( + WinTCWelcomeUserList* user_list, + GtkWidget* widget +) +{ + gtk_widget_set_parent(widget, GTK_WIDGET(user_list)); + + user_list->priv->child_widgets = + g_slist_append(user_list->priv->child_widgets, widget); +} + +static void wintc_welcome_user_list_select_user( + WinTCWelcomeUserList* user_list, + gint index +) +{ + user_list->priv->selected_index = index; + + if (index >= 0) + { + gtk_widget_set_sensitive( + user_list->priv->entry_password, + TRUE + ); + gtk_widget_grab_focus( + user_list->priv->entry_password + ); + } + else + { + gtk_widget_set_sensitive( + user_list->priv->entry_password, + FALSE + ); + } + + gtk_widget_queue_allocate(GTK_WIDGET(user_list)); +} + +static void wintc_welcome_user_list_set_vadjustment( + WinTCWelcomeUserList* user_list, + GtkAdjustment* adjustment +) +{ + if (user_list->priv->vadjustment == adjustment) + { + return; + } + + if (user_list->priv->vadjustment) + { + g_signal_handlers_disconnect_by_func( + user_list->priv->vadjustment, + on_self_adjustment_changed, + user_list + ); + + g_object_unref(user_list->priv->vadjustment); + } + + if (adjustment == NULL) + { + adjustment = + gtk_adjustment_new( + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f + ); + } + + g_signal_connect( + adjustment, + "value-changed", + G_CALLBACK(on_self_adjustment_changed), + user_list + ); + + user_list->priv->vadjustment = g_object_ref_sink(adjustment); + + wintc_welcome_user_list_set_vadjustment_values(user_list); + + g_object_notify(G_OBJECT(user_list), "vadjustment"); +} + +static void wintc_welcome_user_list_set_vadjustment_values( + WinTCWelcomeUserList* user_list +) +{ + GtkAllocation alloc; + gdouble new_upper; + gdouble new_value; + gdouble old_value; + gint user_count; + + gtk_widget_get_allocation(GTK_WIDGET(user_list), &alloc); + + old_value = + gtk_adjustment_get_value(user_list->priv->vadjustment); + user_count = + g_list_length(user_list->priv->users); + + new_upper = + MAX(alloc.height, user_list->priv->item_height * user_count); + + g_object_set( + user_list->priv->vadjustment, + "lower", 0.0, + "upper", new_upper, + "page-size", (gdouble) alloc.height, + "step-increment", alloc.height * 0.1, + "page-increment", alloc.height * 0.9, + NULL + ); + + new_value = CLAMP(old_value, 0, new_upper - alloc.height); + + if (new_value != old_value) + { + gtk_adjustment_set_value( + user_list->priv->vadjustment, + new_value + ); + } +} + + +static void draw_user( + cairo_t* cr, + LightDMUser* user, + const gboolean selected, + const WinTCWelcomeUserList* user_list +) +{ + //const gchar* text = lightdm_user_get_name(user); + double origin_y; + + cairo_get_current_point(cr, NULL, &origin_y); + + // Render usersel + // + if (selected) + { + cairo_set_source_surface( + cr, + user_list->priv->surface_usersel, + 0.0f, + origin_y + ); + cairo_pattern_set_extend( + cairo_get_source(cr), + CAIRO_EXTEND_NONE + ); + cairo_paint(cr); + } + + // Render userpic + // + cairo_set_source_surface( + cr, + selected ? + user_list->priv->surface_tilehot : + user_list->priv->surface_tile, + USER_TILE_OFFSET_X, + USER_TILE_OFFSET_Y + origin_y + ); + cairo_paint(cr); + + cairo_set_source_surface( + cr, + user_list->priv->surface_userpic, + USER_TILE_OFFSET_X + USER_PIC_OFFSET, + USER_TILE_OFFSET_Y + USER_PIC_OFFSET + origin_y + ); + cairo_paint(cr); + + // Text handling + // + cairo_set_source_rgb(cr, 1.0f, 1.0f, 1.0f); + cairo_move_to( + cr, + USER_NAME_OFFSET_X, + USER_NAME_OFFSET_Y + origin_y + ); + cairo_select_font_face( + cr, + "Arial", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL + ); + cairo_set_font_size(cr, 14.0f); + cairo_show_text(cr, lightdm_user_get_name(user)); +} + +// +// CALLBACKS +// +static void on_self_adjustment_changed( + WINTC_UNUSED(GtkAdjustment* adjustment), + WinTCWelcomeUserList* user_list +) +{ + gtk_widget_queue_resize(GTK_WIDGET(user_list)); +} + +static void on_logon_session_attempt_complete( + WINTC_UNUSED(WinTCGinaLogonSession* logon_session), + WinTCGinaResponse response, + gpointer user_data +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(user_data); + + if (response == WINTC_GINA_RESPONSE_FAIL) + { + gtk_entry_set_text( + GTK_ENTRY(user_list->priv->entry_password), + "" + ); + + gtk_widget_set_sensitive( + user_list->priv->button_go, + TRUE + ); + gtk_widget_set_sensitive( + user_list->priv->entry_password, + TRUE + ); + } +} + +static void on_button_go_clicked( + WINTC_UNUSED(GtkButton* self), + gpointer user_data +) +{ + WinTCWelcomeUserList* user_list = WINTC_WELCOME_USER_LIST(user_data); + + gtk_widget_set_sensitive( + user_list->priv->button_go, + FALSE + ); + gtk_widget_set_sensitive( + user_list->priv->entry_password, + FALSE + ); + + wintc_gina_logon_session_try_logon( + user_list->priv->logon_session, + lightdm_user_get_name( + g_list_nth( + user_list->priv->users, + user_list->priv->selected_index + )->data + ), + gtk_entry_get_text(GTK_ENTRY(user_list->priv->entry_password)) + ); +} \ No newline at end of file diff --git a/base/logonui/src/welcome/userlist.h b/base/logonui/src/welcome/userlist.h new file mode 100644 index 0000000..a53d671 --- /dev/null +++ b/base/logonui/src/welcome/userlist.h @@ -0,0 +1,31 @@ +#ifndef __USERLIST_H__ +#define __USERLIST_H__ + +#include +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCWelcomeUserListPrivate WinTCWelcomeUserListPrivate; +typedef struct _WinTCWelcomeUserListClass WinTCWelcomeUserListClass; +typedef struct _WinTCWelcomeUserList WinTCWelcomeUserList; + +#define TYPE_WINTC_WELCOME_USER_LIST (wintc_welcome_user_list_get_type()) +#define WINTC_WELCOME_USER_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_WELCOME_USER_LIST, WinTCWelcomeUserList)) +#define WINTC_WELCOME_USER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_WELCOME_USER_LIST, WinTCWelcomeUserList)) +#define IS_WINTC_WELCOME_USER_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_WELCOME_USER_LIST)) +#define IS_WINTC_WELCOME_USER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_WELCOME_USER_LIST)) +#define WINTC_WELCOME_USER_LIST_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_WELCOME_USER_LIST)) + +GType wintc_welcome_user_list_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_welcome_user_list_new( + WinTCGinaLogonSession* logon_session +); + +#endif diff --git a/base/logonui/src/window.c b/base/logonui/src/window.c new file mode 100644 index 0000000..6edf972 --- /dev/null +++ b/base/logonui/src/window.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include + +#include "window.h" +#include "classic/ui.h" +#include "welcome/ui.h" + +// +// PRIVATE ENUMS +// +enum +{ + PROP_PREFER_CLASSIC_LOGON = 1, + N_PROPERTIES +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCLogonUIWindowPrivate +{ + GtkWidget* login_ui; +}; + +struct _WinTCLogonUIWindowClass +{ + GtkWindowClass __parent__; +}; + +struct _WinTCLogonUIWindow +{ + GtkWindow __parent__; + + WinTCLogonUIWindowPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_logonui_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static gboolean on_window_map_event( + GtkWidget* self, + GdkEventAny* event, + gpointer user_data +); +static void on_window_realize( + GtkWidget* self, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCLogonUIWindow, + wintc_logonui_window, + GTK_TYPE_WINDOW, + G_ADD_PRIVATE(WinTCLogonUIWindow) +) + +static void wintc_logonui_window_class_init( + WinTCLogonUIWindowClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->set_property = wintc_logonui_window_set_property; + + g_object_class_install_property( + object_class, + PROP_PREFER_CLASSIC_LOGON, + g_param_spec_boolean( + "prefer-classic-logon", + "PreferClassicLogon", + "Prefer to use the classic logon user-interface.", + FALSE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); +} + +static void wintc_logonui_window_init( + WinTCLogonUIWindow* self +) +{ + self->priv = wintc_logonui_window_get_instance_private(self); + + // Set up window + // + GdkDisplay* display = gdk_display_get_default(); + GdkRectangle geometry; + GdkMonitor* monitor = gdk_display_get_primary_monitor(display); + + gdk_monitor_get_geometry(monitor, &geometry); + + gtk_widget_set_size_request( + GTK_WIDGET(self), + geometry.width, + geometry.height + ); + gtk_window_move( + GTK_WINDOW(self), + geometry.x, + geometry.y + ); + + // We delay some stuff until realize/map events: + // - we do not add the welcome/classic UI widget until we + // hit the 'map-event' signal, this ensures that any child + // windows are created after this window + // - delay cursor / other init until we realize the GDK + // resources for the window + // + g_signal_connect( + self, + "map-event", + G_CALLBACK(on_window_map_event), + NULL + ); + g_signal_connect( + self, + "realize", + G_CALLBACK(on_window_realize), + NULL + ); + + // + // Welcome/Classic UI is decided/init'd in set_property + // +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_logonui_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCLogonUIWindow* window = WINTC_LOGONUI_WINDOW(object); + + switch (prop_id) + { + case PROP_PREFER_CLASSIC_LOGON: + if (g_value_get_boolean(value)) + { + window->priv->login_ui = + wintc_classic_ui_new(); + } + else + { + window->priv->login_ui = + wintc_welcome_ui_new(); + } + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_logonui_window_new() +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_LOGONUI_WINDOW, + "type", GTK_WINDOW_TOPLEVEL, + "decorated", FALSE, + "resizable", FALSE, + "type-hint", GDK_WINDOW_TYPE_HINT_DESKTOP, + "prefer-classic-logon", FALSE, + NULL + ) + ); +} + +// +// CALLBACKS +// +static gboolean on_window_map_event( + GtkWidget* self, + WINTC_UNUSED(GdkEventAny* event), + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCLogonUIWindow* window = WINTC_LOGONUI_WINDOW(self); + + gtk_container_add( + GTK_CONTAINER(self), + window->priv->login_ui + ); + gtk_widget_show_all(window->priv->login_ui); + + return TRUE; +} + +static void on_window_realize( + GtkWidget* self, + WINTC_UNUSED(gpointer user_data) +) +{ + gdk_window_set_cursor( + gtk_widget_get_window(self), + gdk_cursor_new_for_display( + gdk_display_get_default(), + GDK_LEFT_PTR + ) + ); +} \ No newline at end of file diff --git a/base/logonui/src/window.h b/base/logonui/src/window.h new file mode 100644 index 0000000..9713094 --- /dev/null +++ b/base/logonui/src/window.h @@ -0,0 +1,28 @@ +#ifndef __WINDOW_H__ +#define __WINDOW_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCLogonUIWindowPrivate WinTCLogonUIWindowPrivate; +typedef struct _WinTCLogonUIWindowClass WinTCLogonUIWindowClass; +typedef struct _WinTCLogonUIWindow WinTCLogonUIWindow; + +#define TYPE_WINTC_LOGONUI_WINDOW (wintc_logonui_window_get_type()) +#define WINTC_LOGONUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_LOGONUI_WINDOW, WinTCLogonUIWindow)) +#define WINTC_LOGONUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_LOGONUI_WINDOW, WinTCLogonUIWindow)) +#define IS_WINTC_LOGONUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_LOGONUI_WINDOW)) +#define IS_WINTC_LOGONUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_LOGONUI_WINDOW)) +#define WINTC_LOGONUI_WINDOW_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_LOGONUI_WINDOW)) + +GType wintc_logonui_window_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_logonui_window_new(); + +#endif diff --git a/base/logonui/wintc-logonui.desktop b/base/logonui/wintc-logonui.desktop new file mode 100644 index 0000000..ecbe5d0 --- /dev/null +++ b/base/logonui/wintc-logonui.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=WinTC LogonUI Greeter +Comment=This is the Windows Total Conversion logon user interface. +Exec=logonui +Type=Application diff --git a/packaging/chkdeps.sh b/packaging/chkdeps.sh index d085a3b..223d56d 100755 --- a/packaging/chkdeps.sh +++ b/packaging/chkdeps.sh @@ -162,7 +162,7 @@ check_deps() # case "${dist_id}" in archpkg) - pacman -Q -k "${pkg_name}" >/dev/null 2>&1 + pacman -Q -i "${pkg_name}" >/dev/null 2>&1 ;; deb) dpkg -s "${pkg_name}" >/dev/null 2>&1 diff --git a/packaging/cmake-inc/common/CMakeLists.txt b/packaging/cmake-inc/common/CMakeLists.txt index ec31829..c094513 100644 --- a/packaging/cmake-inc/common/CMakeLists.txt +++ b/packaging/cmake-inc/common/CMakeLists.txt @@ -21,6 +21,10 @@ option( # Define pre-processor macro for checked builds # +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + if (${CMAKE_BUILD_TYPE} STREQUAL Debug) add_definitions(-DWINTC_CHECKED) endif() diff --git a/packaging/targets b/packaging/targets index de03a62..5939208 100644 --- a/packaging/targets +++ b/packaging/targets @@ -1,5 +1,6 @@ base/bootvid base/bldtag +base/logonui cursors/no-shadow/standard cursors/with-shadow/standard fonts diff --git a/shared/comctl/CMakeLists.txt b/shared/comctl/CMakeLists.txt index 4c8105f..759f1bb 100644 --- a/shared/comctl/CMakeLists.txt +++ b/shared/comctl/CMakeLists.txt @@ -21,14 +21,18 @@ include(../../packaging/cmake-inc/linking/CMakeLists.txt) include(../../packaging/cmake-inc/packaging/CMakeLists.txt) include(../../packaging/cmake-inc/resources/CMakeLists.txt) -wintc_resolve_library(glib-2.0 GLIB) -wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF) +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) wintc_compile_resources() add_library( libwintc-comctl src/resources.c + src/animctl.c + src/animctl.h src/style.c src/style.h ) @@ -49,20 +53,26 @@ target_compile_options( target_include_directories( libwintc-comctl SYSTEM + PRIVATE ${GDK_PIXBUF_INCLUDE_DIRS} PRIVATE ${GLIB_INCLUDE_DIRS} PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} ) target_link_directories( libwintc-comctl + PRIVATE ${GDK_PIXBUF_LIBRARY_DIRS} PRIVATE ${GLIB_LIBRARY_DIRS} PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} ) target_link_libraries( libwintc-comctl + PRIVATE ${GDK_PIXBUF_LIBRARIES} PRIVATE ${GLIB_LIBRARIES} PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} ) # Installation diff --git a/shared/comctl/deps b/shared/comctl/deps index 404df23..7e95f86 100644 --- a/shared/comctl/deps +++ b/shared/comctl/deps @@ -1,2 +1,4 @@ +bt,rt:gdk-pixbuf2 bt,rt:glib2 bt,rt:gtk3 +bt,rt:wintc-comgtk diff --git a/shared/comctl/public/wintc-comctl.h b/shared/comctl/public/wintc-comctl.h index 236889b..1d5ca3c 100644 --- a/shared/comctl/public/wintc-comctl.h +++ b/shared/comctl/public/wintc-comctl.h @@ -1,10 +1,72 @@ #ifndef __WINTC_COMCTL_H__ #define __WINTC_COMCTL_H__ +#include +#include +#include + +// +// Animation control +// +#define WINTC_ANIMATION_INFINITE 0 +#define WINTC_ANIMATION_NONE 0 + +typedef struct _WinTCAnimationPrivate WinTCAnimationPrivate; +typedef struct _WinTCAnimationClass WinTCAnimationClass; +typedef struct _WinTCAnimation WinTCAnimation; + +#define TYPE_WINTC_ANIMATION (wintc_animation_get_type()) +#define WINTC_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_ANIMATION, WinTCAnimation)) +#define WINTC_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_ANIMATION, WinTCAnimation)) +#define IS_WINTC_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_ANIMATION)) +#define IS_WINTC_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_ANIMATION)) +#define WINTC_ANIMATION_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_ANIMATION)) + +GType wintc_animation_get_type(void) G_GNUC_CONST; + +GtkWidget* wintc_animation_new(void); + +guint wintc_animation_add_framesheet( + WinTCAnimation* anim, + GdkPixbuf* framesheet_pixbuf, + gint frame_count +); +guint wintc_animation_add_static( + WinTCAnimation* anim, + GdkPixbuf* static_pixbuf +); +guint wintc_animation_get_count( + WinTCAnimation* anim +); +GtkAlign wintc_animation_get_halign( + WinTCAnimation* anim +); +GtkAlign wintc_animation_get_valign( + WinTCAnimation* anim +); +void wintc_animation_play( + WinTCAnimation* anim, + guint id, + gint frame_rate, + gint repeats +); +void wintc_animation_remove( + WinTCAnimation* anim, + guint id +); +void wintc_animation_set_halign( + WinTCAnimation* anim, + GtkAlign align +); +void wintc_animation_set_valign( + WinTCAnimation* anim, + GtkAlign align +); + // // Default CSS stuff // -#define WINTC_COMCTL_BUTTON_BOX_CLASS "wintc-button-box" +#define WINTC_COMCTL_BUTTON_BOX_CSS_CLASS "wintc-button-box" void wintc_comctl_install_default_styles(void); diff --git a/shared/comctl/src/animctl.c b/shared/comctl/src/animctl.c new file mode 100644 index 0000000..db22b15 --- /dev/null +++ b/shared/comctl/src/animctl.c @@ -0,0 +1,739 @@ +#include +#include +#include +#include +#include + +#include "animctl.h" + +#define ONE_SECOND_IN_US 1000000 + +// +// PRIVATE ENUMS +// +enum +{ + PROP_HALIGN = 1, + PROP_VALIGN, + N_PROPERTIES +}; + +// +// PRIVATE STURCTURE DEFINITIONS +// +typedef struct _WinTCAnimationData +{ + GdkPixbuf* pixbuf_bmp; + cairo_surface_t* surface_bmp; + + gint frame_count; + gint frame_height; +} WinTCAnimationData; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCAnimationPrivate +{ + GSList* animations; + + gboolean is_animating; + guint tick_id; + + WinTCAnimationData* current_anim; + gint current_frame; + gint desired_repeats; + gint last_frame; + gint64 origin_frame_time; + gint64 per_frame_time; + gint64 playback_total_time; + + GtkAlign halign; + GtkAlign valign; +}; + +struct _WinTCAnimationClass +{ + GtkWidgetClass __parent__; +}; + +struct _WinTCAnimation +{ + GtkWidget __parent__; + + WinTCAnimationPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_animation_finalize( + GObject* gobject +); +static void wintc_animation_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +); +static void wintc_animation_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static gboolean wintc_animation_draw( + GtkWidget* widget, + cairo_t* cr +); +static void wintc_animation_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +); +static void wintc_animation_get_preferred_height_for_width( + GtkWidget* widget, + gint width, + gint* minimum_height, + gint* natural_height +); +static void wintc_animation_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +); +static void wintc_animation_get_preferred_width_for_height( + GtkWidget* widget, + gint height, + gint* minimum_width, + gint* natural_width +); + +static void free_anim_data( + WinTCAnimationData* anim_data +); + +static gboolean wintc_animation_step( + GtkWidget* widget, + GdkFrameClock* frame_clock, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCAnimation, + wintc_animation, + GTK_TYPE_WIDGET, + G_ADD_PRIVATE(WinTCAnimation) +) + +static void wintc_animation_class_init( + WinTCAnimationClass* klass +) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_animation_finalize; + object_class->get_property = wintc_animation_get_property; + object_class->set_property = wintc_animation_set_property; + + widget_class->draw = + wintc_animation_draw; + widget_class->get_preferred_height = + wintc_animation_get_preferred_height; + widget_class->get_preferred_height_for_width = + wintc_animation_get_preferred_height_for_width; + widget_class->get_preferred_width = + wintc_animation_get_preferred_width; + widget_class->get_preferred_width_for_height = + wintc_animation_get_preferred_width_for_height; + + g_object_class_install_property( + object_class, + PROP_HALIGN, + g_param_spec_enum( + "gfx-halign", + "GfxHAlign", + "The horizontal alignment for drawing.", + GTK_TYPE_ALIGN, + GTK_ALIGN_CENTER, + G_PARAM_WRITABLE + ) + ); + g_object_class_install_property( + object_class, + PROP_VALIGN, + g_param_spec_enum( + "gfx-valign", + "GfxVAlign", + "The vertical alignment for drawing.", + GTK_TYPE_ALIGN, + GTK_ALIGN_CENTER, + G_PARAM_WRITABLE + ) + ); +} + +static void wintc_animation_init( + WinTCAnimation* self +) +{ + self->priv = wintc_animation_get_instance_private(self); + + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_animation_finalize( + GObject* gobject +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(gobject); + + g_slist_free_full( + anim->priv->animations, + (GDestroyNotify) free_anim_data + ); + + if (anim->priv->tick_id > 0) + { + gtk_widget_remove_tick_callback( + GTK_WIDGET(anim), + anim->priv->tick_id + ); + } + + (G_OBJECT_CLASS(wintc_animation_parent_class))->finalize(gobject); +} + +static void wintc_animation_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(object); + + switch (prop_id) + { + case PROP_HALIGN: + g_value_set_enum(value, anim->priv->halign); + break; + + case PROP_VALIGN: + g_value_set_enum(value, anim->priv->valign); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void wintc_animation_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(object); + + switch (prop_id) + { + case PROP_HALIGN: + anim->priv->halign = g_value_get_enum(value); + gtk_widget_queue_draw(GTK_WIDGET(object)); + break; + + case PROP_VALIGN: + anim->priv->valign = g_value_get_enum(value); + gtk_widget_queue_draw(GTK_WIDGET(object)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gboolean wintc_animation_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(widget); + + if (anim->priv->current_anim == NULL) + { + return FALSE; + } + + // Set up to render the graphic + // + gint graphic_height; + gint graphic_width; + double y_offset; + + graphic_width = + cairo_image_surface_get_width( + anim->priv->current_anim->surface_bmp + ); + + if (anim->priv->current_anim->frame_count > 1) + { + graphic_height = anim->priv->current_anim->frame_height; + y_offset = (graphic_height * anim->priv->current_frame) * -1.0f; + } + else + { + graphic_height = + cairo_image_surface_get_height( + anim->priv->current_anim->surface_bmp + ); + y_offset = 0.0f; + } + + // Calculate where we're drawing... + // + gint my_height = gtk_widget_get_allocated_height(widget); + gint my_width = gtk_widget_get_allocated_width(widget); + + double target_x; + double target_y; + double target_width = graphic_width; + double target_height = graphic_height; + double target_scale_w = 1.0f; + double target_scale_h = 1.0f; + + switch (anim->priv->halign) + { + case GTK_ALIGN_END: + target_x = (double) (my_width - graphic_width); + break; + + case GTK_ALIGN_CENTER: + target_x = (double) ((my_width / 2) - (graphic_width / 2)); + break; + + case GTK_ALIGN_FILL: + target_width = my_width; + target_scale_w = (double) my_width / (double) graphic_width; + // Fall-through + + default: + target_x = 0.0f; + break; + } + + switch (anim->priv->valign) + { + case GTK_ALIGN_END: + target_y = (double) (my_height - graphic_height); + break; + + case GTK_ALIGN_CENTER: + target_y = (double) ((my_height / 2) - (graphic_height / 2)); + break; + + case GTK_ALIGN_FILL: + target_height = my_height; + target_scale_h = (double) my_height / (double) graphic_height; + // Fall-through + + default: + target_y = 0.0f; + break; + } + + // Draw now + // + cairo_save(cr); + + cairo_translate(cr, target_x, target_y); + + cairo_rectangle( + cr, + 0.0f, + 0.0f, + (double) target_width, + (double) target_height + ); + cairo_clip(cr); + + cairo_scale(cr, target_scale_w, target_scale_h); + + cairo_set_source_surface( + cr, + anim->priv->current_anim->surface_bmp, + 0.0f, + y_offset + ); + cairo_pattern_set_extend( + cairo_get_source(cr), + CAIRO_EXTEND_NONE + ); + + cairo_paint(cr); + + cairo_restore(cr); + + anim->priv->last_frame = anim->priv->current_frame; + + return FALSE; +} + +static void wintc_animation_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(widget); + gint height = 0; + + if (anim->priv->current_anim != NULL) + { + if (anim->priv->current_anim->frame_count == 1) + { + height = + cairo_image_surface_get_height( + anim->priv->current_anim->surface_bmp + ); + } + else + { + height = anim->priv->current_anim->frame_height; + } + } + + *minimum_height = height; + *natural_height = height; +} + +static void wintc_animation_get_preferred_height_for_width( + GtkWidget* widget, + WINTC_UNUSED(gint width), + gint* minimum_height, + gint* natural_height +) +{ + wintc_animation_get_preferred_height( + widget, + minimum_height, + natural_height + ); +} + +static void wintc_animation_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(widget); + + if (anim->priv->current_anim == NULL) + { + minimum_width = 0; + natural_width = 0; + return; + } + + gint width = + cairo_image_surface_get_width( + anim->priv->current_anim->surface_bmp + ); + + *minimum_width = width; + *natural_width = width; +} + +static void wintc_animation_get_preferred_width_for_height( + GtkWidget* widget, + WINTC_UNUSED(gint height), + gint* minimum_width, + gint* natural_width +) +{ + wintc_animation_get_preferred_width( + widget, + minimum_width, + natural_width + ); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_animation_new(void) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_ANIMATION, + NULL + ) + ); +} + +guint wintc_animation_add_framesheet( + WinTCAnimation* anim, + GdkPixbuf* framesheet_pixbuf, + gint frame_count +) +{ + WinTCAnimationData* anim_data = g_new0(WinTCAnimationData, 1); + gint height = gdk_pixbuf_get_height(framesheet_pixbuf); + + g_object_ref(framesheet_pixbuf); + + anim_data->pixbuf_bmp = framesheet_pixbuf; + anim_data->surface_bmp = + gdk_cairo_surface_create_from_pixbuf( + framesheet_pixbuf, + 1, + NULL // FIXME: Error reporting + ); + + anim_data->frame_count = frame_count; + anim_data->frame_height = height / frame_count; + + anim->priv->animations = + g_slist_append( + anim->priv->animations, + anim_data + ); + + return wintc_animation_get_count(anim); +} + +guint wintc_animation_add_static( + WinTCAnimation* anim, + GdkPixbuf* static_pixbuf +) +{ + WinTCAnimationData* anim_data = g_new0(WinTCAnimationData, 1); + + g_object_ref(static_pixbuf); + + anim_data->pixbuf_bmp = static_pixbuf; + anim_data->surface_bmp = + gdk_cairo_surface_create_from_pixbuf( + static_pixbuf, + 1, + NULL // FIXME: Error reporting + ); + + anim_data->frame_count = 1; + anim_data->frame_height = 0; + + anim->priv->animations = + g_slist_append( + anim->priv->animations, + anim_data + ); + + return wintc_animation_get_count(anim); +} + +guint wintc_animation_get_count( + WinTCAnimation* anim +) +{ + return g_slist_length(anim->priv->animations); +} + +GtkAlign wintc_animation_get_halign( + WinTCAnimation* anim +) +{ + GtkAlign value; + + g_object_get(anim, "gfx-halign", &value, NULL); + + return value; +} + +GtkAlign wintc_animation_get_valign( + WinTCAnimation* anim +) +{ + GtkAlign value; + + g_object_get(anim, "gfx-valign", &value, NULL); + + return value; +} + +void wintc_animation_play( + WinTCAnimation* anim, + guint id, + gint frame_rate, + gint repeats +) +{ + // Reset anim state values + // + anim->priv->is_animating = FALSE; + + anim->priv->current_anim = NULL; + anim->priv->current_frame = 0; + anim->priv->last_frame = 0; + anim->priv->desired_repeats = 0; + anim->priv->origin_frame_time = 0; + anim->priv->per_frame_time = 0; + anim->priv->playback_total_time = 0; + + // Queue a draw regardless of what happens + // + gtk_widget_queue_draw(GTK_WIDGET(anim)); + + // Check we're being asked to actually play anything + // + if (id > wintc_animation_get_count(anim)) + { + g_warning("WinTCAnimation - attempted to play invalid ID %u", id); + return; + } + + if (id == WINTC_ANIMATION_NONE) + { + return; + } + + // Wahey! We have been asked to play something! + // + gint real_id = id - 1; // The consumer is given the count rather than index + // in the 'add' APIs, so that we can have a special + // meaning for 0 (WINTC_ANIMATION_NONE) + + GSList* anim_link = g_slist_nth( + anim->priv->animations, + real_id + ); + WinTCAnimationData* anim_data = anim_link->data; + + anim->priv->current_anim = anim_data; + + if ( + anim_data->frame_count > 1 && + frame_rate > 0 + ) + { + anim->priv->is_animating = TRUE; + + anim->priv->desired_repeats = repeats; + anim->priv->origin_frame_time = g_get_monotonic_time(); + anim->priv->per_frame_time = ONE_SECOND_IN_US / frame_rate; + anim->priv->playback_total_time = + anim->priv->per_frame_time * anim_data->frame_count; + + if (anim->priv->tick_id == 0) + { + gtk_widget_add_tick_callback( + GTK_WIDGET(anim), + (GtkTickCallback) wintc_animation_step, + NULL, + NULL + ); + } + } +} + +void wintc_animation_remove( + WinTCAnimation* anim, + guint id +) +{ + if (id == 0 || id > wintc_animation_get_count(anim)) + { + g_warning("WinTCAnimation - attempted to remove invalid ID %u", id); + return; + } + + gint real_id = id - 1; + GSList* to_delete = g_slist_nth(anim->priv->animations, real_id); + + free_anim_data(to_delete->data); + + anim->priv->animations = + g_slist_delete_link( + anim->priv->animations, + to_delete + ); +} + +void wintc_animation_set_halign( + WinTCAnimation* anim, + GtkAlign align +) +{ + g_object_set(anim, "gfx-halign", align, NULL); +} + +void wintc_animation_set_valign( + WinTCAnimation* anim, + GtkAlign align +) +{ + g_object_set(anim, "gfx-valign", align, NULL); +} + +// +// PRIVATE FUNCTIONS +// +static void free_anim_data( + WinTCAnimationData* anim_data +) +{ + cairo_surface_destroy(anim_data->surface_bmp); + g_clear_object(&(anim_data->pixbuf_bmp)); + g_free(anim_data); +} + +// +// CALLBACKS +// +static gboolean wintc_animation_step( + GtkWidget* widget, + GdkFrameClock* frame_clock, + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCAnimation* anim = WINTC_ANIMATION(widget); + + if (!anim->priv->is_animating) + { + return G_SOURCE_REMOVE; + } + + gint64 frame_time = gdk_frame_clock_get_frame_time(frame_clock); + gint64 progress = frame_time - anim->priv->origin_frame_time; + gint64 repeat_no = progress / anim->priv->playback_total_time; + gint64 within_frame = progress % anim->priv->playback_total_time; + gint frame_no = (gint) (within_frame / anim->priv->per_frame_time); + + if ( + anim->priv->desired_repeats != WINTC_ANIMATION_INFINITE && + anim->priv->desired_repeats < repeat_no + ) + { + return G_SOURCE_REMOVE; + } + + if (anim->priv->last_frame != frame_no) + { + gtk_widget_queue_draw(widget); + } + + anim->priv->current_frame = frame_no; + + return G_SOURCE_CONTINUE; +} \ No newline at end of file diff --git a/shared/comctl/src/animctl.h b/shared/comctl/src/animctl.h new file mode 100644 index 0000000..db65a6a --- /dev/null +++ b/shared/comctl/src/animctl.h @@ -0,0 +1,72 @@ +#ifndef __ANIMCTL_H__ +#define __ANIMCTL_H__ + +#include +#include +#include + +// +// PUBLIC DEFINES +// +#define WINTC_ANIMATION_INFINITE 0 +#define WINTC_ANIMATION_NONE 0 + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCAnimationPrivate WinTCAnimationPrivate; +typedef struct _WinTCAnimationClass WinTCAnimationClass; +typedef struct _WinTCAnimation WinTCAnimation; + +#define TYPE_WINTC_ANIMATION (wintc_animation_get_type()) +#define WINTC_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_ANIMATION, WinTCAnimation)) +#define WINTC_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_ANIMATION, WinTCAnimation)) +#define IS_WINTC_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_ANIMATION)) +#define IS_WINTC_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_ANIMATION)) +#define WINTC_ANIMATION_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_ANIMATION)) + +GType wintc_animation_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_animation_new(void); + +guint wintc_animation_add_framesheet( + WinTCAnimation* anim, + GdkPixbuf* framesheet_pixbuf, + gint frame_count +); +guint wintc_animation_add_static( + WinTCAnimation* anim, + GdkPixbuf* static_pixbuf +); +guint wintc_animation_get_count( + WinTCAnimation* anim +); +GtkAlign wintc_animation_get_halign( + WinTCAnimation* anim +); +GtkAlign wintc_animation_get_valign( + WinTCAnimation* anim +); +void wintc_animation_play( + WinTCAnimation* anim, + guint id, + gint frame_rate, + gint repeats +); +void wintc_animation_remove( + WinTCAnimation* anim, + guint id +); +void wintc_animation_set_halign( + WinTCAnimation* anim, + GtkAlign align +); +void wintc_animation_set_valign( + WinTCAnimation* anim, + GtkAlign align +); + +#endif diff --git a/shared/comgtk/CMakeLists.txt b/shared/comgtk/CMakeLists.txt index 6f883ff..c0ad9cd 100644 --- a/shared/comgtk/CMakeLists.txt +++ b/shared/comgtk/CMakeLists.txt @@ -13,6 +13,10 @@ set(PROJECT_MAINTAINER "Rory Fewell ") set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) +# Necessary due to fn ptr casts in marshal.c +# +set(WINTC_NO_PEDANTIC_COMPILE true) + include(GNUInstallDirs) include(../../packaging/cmake-inc/common/CMakeLists.txt) @@ -35,6 +39,8 @@ add_library( src/gchar.h src/list.c src/list.h + src/marshal.c + src/marshal.h src/msgbox.c src/msgbox.h src/profile.c diff --git a/shared/comgtk/public/wintc-comgtk.h b/shared/comgtk/public/wintc-comgtk.h index 6508d97..0de3376 100644 --- a/shared/comgtk/public/wintc-comgtk.h +++ b/shared/comgtk/public/wintc-comgtk.h @@ -8,7 +8,13 @@ // Debugging // #define WINTC_ENVVAR_DEBUG_LOGGING "WINDEBUG" -#define WINTC_LOG_DEBUG(...) if (getenv(WINTC_ENVVAR_DEBUG_LOGGING)) { g_message(__VA_ARGS__); } +#define WINTC_LOG_USER_DEBUG(...) if (getenv(WINTC_ENVVAR_DEBUG_LOGGING)) { g_message(__VA_ARGS__); } + +#ifdef WINTC_CHECKED +#define WINTC_LOG_DEBUG(...) g_message(__VA_ARGS__); +#else +#define WINTC_LOG_DEBUG(...) +#endif // // Shorthand @@ -101,6 +107,27 @@ GList* wintc_list_read_from_string( const gchar* str ); +// +// Closure Marshals +// +void wintc_cclosure_marshal_BOOLEAN__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint, + gpointer marshal_data +); + +void wintc_cclosure_marshal_INT__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint, + gpointer marshal_data +); + // // Message Box-related // @@ -208,4 +235,4 @@ void wintc_focus_window( GtkWindow* window ); -#endif +#endif \ No newline at end of file diff --git a/shared/comgtk/src/debug.h b/shared/comgtk/src/debug.h index ab90e2a..a02c6a0 100644 --- a/shared/comgtk/src/debug.h +++ b/shared/comgtk/src/debug.h @@ -1,7 +1,17 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ +// Two variants for debug messaging are provided: +// USER_DEBUG is at runtime, intended at end user debugging +// DEBUG is compile time, intended for development +// #define WINTC_ENVVAR_DEBUG_LOGGING "WINDEBUG" -#define WINTC_LOG_DEBUG(...) if (getenv(WINTC_ENVVAR_DEBUG_LOGGING)) { g_message(__VA_ARGS__); } +#define WINTC_LOG_USER_DEBUG(...) if (getenv(WINTC_ENVVAR_DEBUG_LOGGING)) { g_message(__VA_ARGS__); } +#ifdef WINTC_CHECKED +#define WINTC_LOG_DEBUG(...) g_message(__VA_ARGS__); +#else +#define WINTC_LOG_DEBUG(...) #endif + +#endif \ No newline at end of file diff --git a/shared/comgtk/src/marshal.c b/shared/comgtk/src/marshal.c new file mode 100644 index 0000000..9e61676 --- /dev/null +++ b/shared/comgtk/src/marshal.c @@ -0,0 +1,96 @@ +#include +#include + +#include "marshal.h" +#include "shorthand.h" + +// +// PUBLIC FUNCTIONS +// +void wintc_cclosure_marshal_BOOLEAN__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + WINTC_UNUSED(gpointer invocation_hint), + gpointer marshal_data +) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__VOID) ( + gpointer data1, + gpointer data2 + ); + + GMarshalFunc_BOOLEAN__VOID callback; + GCClosure* cc; + gpointer data1; + gpointer data2; + gboolean v_return; + + cc = (GCClosure*) closure; + + g_return_if_fail(n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA(closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer(param_values + 0); + } + else + { + data1 = g_value_peek_pointer(param_values + 0); + data2 = closure->data; + } + + callback = + (GMarshalFunc_BOOLEAN__VOID) + (marshal_data ? marshal_data : cc->callback); + + v_return = callback(data1, data2); + + g_value_set_boolean(return_value, v_return); +} + +void wintc_cclosure_marshal_INT__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + WINTC_UNUSED(gpointer invocation_hint), + gpointer marshal_data +) +{ + typedef gint (*GMarshalFunc_INT__VOID) ( + gpointer data1, + gpointer data2 + ); + + GMarshalFunc_INT__VOID callback; + GCClosure* cc; + gpointer data1; + gpointer data2; + gboolean v_return; + + cc = (GCClosure*) closure; + + g_return_if_fail(n_param_values == 1); + + if (G_CCLOSURE_SWAP_DATA(closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer(param_values + 0); + } + else + { + data1 = g_value_peek_pointer(param_values + 0); + data2 = closure->data; + } + + callback = + (GMarshalFunc_INT__VOID) + (marshal_data ? marshal_data : cc->callback); + + v_return = callback(data1, data2); + + g_value_set_int(return_value, v_return); +} \ No newline at end of file diff --git a/shared/comgtk/src/marshal.h b/shared/comgtk/src/marshal.h new file mode 100644 index 0000000..5b6d6bf --- /dev/null +++ b/shared/comgtk/src/marshal.h @@ -0,0 +1,28 @@ +#ifndef __MARSHAL_H__ +#define __MARSHAL_H__ + +#include +#include + +// +// PUBLIC FUNCTIONS +// +void wintc_cclosure_marshal_BOOLEAN__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint, + gpointer marshal_data +); + +void wintc_cclosure_marshal_INT__VOID( + GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint, + gpointer marshal_data +); + +#endif \ No newline at end of file diff --git a/shared/debian-control.in b/shared/debian-control.in deleted file mode 100644 index 52645a4..0000000 --- a/shared/debian-control.in +++ /dev/null @@ -1,7 +0,0 @@ -Package: lib@PROJECT_NAME@-@PROJECT_VERSION@ -Version: @PROJECT_VERSION@ -Maintainer: Rory Fewell -Architecture: @ARCH_NAME@ -Section: free -Description: @PROJECT_DESCRIPTION@ -Depends: @DEB_DEPENDENCIES@ diff --git a/shared/exec/src/exec.c b/shared/exec/src/exec.c index c35ce36..dc75f78 100644 --- a/shared/exec/src/exec.c +++ b/shared/exec/src/exec.c @@ -76,7 +76,7 @@ gboolean wintc_launch_command( WINTC_SAFE_REF_CLEAR(out_error); - WINTC_LOG_DEBUG("Launching %s", cmdline); + WINTC_LOG_USER_DEBUG("Launching %s", cmdline); // Iterate through parsers // @@ -93,7 +93,7 @@ gboolean wintc_launch_command( return FALSE; } - WINTC_LOG_DEBUG("Parse result: %s", real_cmdline); + WINTC_LOG_USER_DEBUG("Parse result: %s", real_cmdline); wintc_strsteal(&tmp_cmdline, &real_cmdline); @@ -104,7 +104,7 @@ gboolean wintc_launch_command( // real_cmdline = tmp_cmdline; - WINTC_LOG_DEBUG("Parsed command line: %s", real_cmdline); + WINTC_LOG_USER_DEBUG("Parsed command line: %s", real_cmdline); // Parse the command line into an argument vector // @@ -142,14 +142,14 @@ gboolean wintc_launch_command( if (!success) { - WINTC_LOG_DEBUG("Failed to launch: %s", error->message); + WINTC_LOG_USER_DEBUG("Failed to launch: %s", error->message); g_propagate_error(out_error, error); return FALSE; } - WINTC_LOG_DEBUG("Done."); + WINTC_LOG_USER_DEBUG("Done."); return TRUE; } @@ -201,7 +201,7 @@ static gboolean parse_file_in_cmdline( g_strcmp0(file_mime, "application/x-sharedlib") != 0 ) { - WINTC_LOG_DEBUG("Not an executable, will look for handler."); + WINTC_LOG_USER_DEBUG("Not an executable, will look for handler."); handler_entry = wintc_query_mime_handler( @@ -211,7 +211,7 @@ static gboolean parse_file_in_cmdline( if (handler_entry == NULL) { - WINTC_LOG_DEBUG("I have nothing to handle the file!"); + WINTC_LOG_USER_DEBUG("I have nothing to handle the file!"); g_propagate_error(out_error, error); WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline)); @@ -282,7 +282,7 @@ static gboolean parse_unc_path_in_cmdline( if (unc_regex == NULL) { - WINTC_LOG_DEBUG("Failed to create the UNC path regex."); + WINTC_LOG_USER_DEBUG("Failed to create the UNC path regex."); g_propagate_error(out_error, error); @@ -292,7 +292,7 @@ static gboolean parse_unc_path_in_cmdline( // Examine command line // - WINTC_LOG_DEBUG("Checking if %s looks like a UNC path...", cmdline); + WINTC_LOG_USER_DEBUG("Checking if %s looks like a UNC path...", cmdline); g_regex_match(unc_regex, cmdline, 0, &match_info); @@ -302,7 +302,7 @@ static gboolean parse_unc_path_in_cmdline( // if (match_count == 0) { - WINTC_LOG_DEBUG("Nope!"); + WINTC_LOG_USER_DEBUG("Nope!"); g_match_info_free(match_info); @@ -313,7 +313,7 @@ static gboolean parse_unc_path_in_cmdline( // Command line IS a UNC path, we need to retrieve the target host and path // - WINTC_LOG_DEBUG("Yeah, looks like a UNC path."); + WINTC_LOG_USER_DEBUG("Yeah, looks like a UNC path."); host = g_strdup(g_match_info_fetch(match_info, 1)); @@ -329,8 +329,8 @@ static gboolean parse_unc_path_in_cmdline( g_match_info_free(match_info); - // Construct URI (doesn't matter if path is NULL, the func stops at the first - // NULL anyway) + // Construct URI (doesn't matter if path is NULL, the func stops at the + // first NULL anyway) // uri = g_strconcat("smb://", host, path, NULL); @@ -376,7 +376,7 @@ static gboolean parse_url_in_cmdline( if (url_regex == NULL) { - WINTC_LOG_DEBUG("Failed to create the URL regex."); + WINTC_LOG_USER_DEBUG("Failed to create the URL regex."); g_propagate_error(out_error, error); @@ -386,7 +386,7 @@ static gboolean parse_url_in_cmdline( // Examine command line // - WINTC_LOG_DEBUG("Checking if %s looks like a URL...", cmdline); + WINTC_LOG_USER_DEBUG("Checking if %s looks like a URL...", cmdline); g_regex_match(url_regex, cmdline, 0, &match_info); @@ -394,7 +394,7 @@ static gboolean parse_url_in_cmdline( // if (g_match_info_get_match_count(match_info) == 0) { - WINTC_LOG_DEBUG("Nope!"); + WINTC_LOG_USER_DEBUG("Nope!"); g_match_info_free(match_info); @@ -405,7 +405,7 @@ static gboolean parse_url_in_cmdline( // Command line IS a URL, retrieve the scheme, query the handling program... // - WINTC_LOG_DEBUG("Yeah, looks like a URL."); + WINTC_LOG_USER_DEBUG("Yeah, looks like a URL."); uri_scheme = g_match_info_fetch(match_info, 1); mime_type = g_strdup_printf( @@ -421,7 +421,7 @@ static gboolean parse_url_in_cmdline( if (handler_entry == NULL) { - WINTC_LOG_DEBUG("I have nothing to handle the URL!"); + WINTC_LOG_USER_DEBUG("I have nothing to handle the URL!"); g_propagate_error(out_error, error); @@ -476,7 +476,7 @@ static gchar** true_shell_parse_argv( if (error != NULL) { - WINTC_LOG_DEBUG("Failed to parse command line: %s", cmdline); + WINTC_LOG_USER_DEBUG("Failed to parse command line: %s", cmdline); g_propagate_error(out_error, error); diff --git a/shared/exec/src/mime.c b/shared/exec/src/mime.c index 5e1616a..baf1d1d 100644 --- a/shared/exec/src/mime.c +++ b/shared/exec/src/mime.c @@ -24,7 +24,7 @@ gchar* wintc_query_mime_for_file( gint status; gboolean success = FALSE; - WINTC_LOG_DEBUG("Querying MIME type for: %s", filepath); + WINTC_LOG_USER_DEBUG("Querying MIME type for: %s", filepath); WINTC_SAFE_REF_CLEAR(out_error); @@ -47,7 +47,7 @@ gchar* wintc_query_mime_for_file( { g_strstrip(cmd_output); - WINTC_LOG_DEBUG("Determined: %s", cmd_output); + WINTC_LOG_USER_DEBUG("Determined: %s", cmd_output); return cmd_output; } @@ -56,14 +56,14 @@ gchar* wintc_query_mime_for_file( // if (error != NULL) { - WINTC_LOG_DEBUG("An error occurred: %s", error->message); + WINTC_LOG_USER_DEBUG("An error occurred: %s", error->message); g_propagate_error(out_error, error); return NULL; } - WINTC_LOG_DEBUG("Failed with code %d", status); + WINTC_LOG_USER_DEBUG("Failed with code %d", status); switch (status) { @@ -109,7 +109,7 @@ GDesktopAppInfo* wintc_query_mime_handler( gchar* filename = NULL; gboolean success = FALSE; - WINTC_LOG_DEBUG("Querying handler for MIME type %s", mime_query); + WINTC_LOG_USER_DEBUG("Querying handler for MIME type %s", mime_query); WINTC_SAFE_REF_CLEAR(out_error); @@ -128,7 +128,7 @@ GDesktopAppInfo* wintc_query_mime_handler( if (!success) { - WINTC_LOG_DEBUG("Failed to query MIME type: %s", error->message); + WINTC_LOG_USER_DEBUG("Failed to query MIME type: %s", error->message); g_propagate_error(out_error, error); @@ -143,7 +143,7 @@ GDesktopAppInfo* wintc_query_mime_handler( if (output_length == 0) { - WINTC_LOG_DEBUG("No handler found!"); + WINTC_LOG_USER_DEBUG("No handler found!"); g_set_error( out_error, @@ -167,7 +167,7 @@ GDesktopAppInfo* wintc_query_mime_handler( ); entry = g_desktop_app_info_new(filename); - WINTC_LOG_DEBUG("Query returned: %s", filename); + WINTC_LOG_USER_DEBUG("Query returned: %s", filename); g_free(cmd_output); g_free(filename); @@ -182,7 +182,7 @@ GDesktopAppInfo* wintc_query_mime_handler( mime_query ); - WINTC_LOG_DEBUG("Association found, but the desktop entry doesn't exist."); + WINTC_LOG_USER_DEBUG("Association found, but the desktop entry doesn't exist."); return NULL; } diff --git a/shared/msgina/CMakeLists.txt b/shared/msgina/CMakeLists.txt new file mode 100644 index 0000000..3ab3e9b --- /dev/null +++ b/shared/msgina/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.0) + +project( + libwintc-msgina + VERSION 1.0 + DESCRIPTION "Windows Total Conversion GINA library." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS false) +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/libraries/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +include(../../packaging/cmake-inc/resources/CMakeLists.txt) + +wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF) +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(liblightdm-gobject-1 LIGHTDM) +wintc_resolve_library(wintc-comctl WINTC_COMCTL) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-winbrand WINTC_WINBRAND) + +wintc_compile_resources() + +add_library( + libwintc-msgina + src/resources.c + src/authwnd.c + src/authwnd.h + src/challenge.h + src/logon.c + src/logon.h + src/state.h + src/stripctl.c + src/stripctl.h +) + +set_target_properties( + libwintc-msgina + PROPERTIES + PUBLIC_HEADER public/wintc-msgina.h + SOVERSION 1 + VERSION ${PROJECT_VERSION} +) + +target_compile_options( + libwintc-msgina + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + libwintc-msgina + SYSTEM + PRIVATE ${GDK_PIXBUF_INCLUDE_DIRS} + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${LIGHTDM_INCLUDE_DIRS} + PRIVATE ${WINTC_COMCTL_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_WINBRAND_INCLUDE_DIRS} +) + +target_link_directories( + libwintc-msgina + PRIVATE ${GDK_PIXBUF_LIBRARY_DIRS} + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${LIGHTDM_LIBRARY_DIRS} + PRIVATE ${WINTC_COMCTL_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_WINBRAND_LIBRARY_DIRS} +) + +target_link_libraries( + libwintc-msgina + PRIVATE ${GDK_PIXBUF_LIBRARIES} + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${LIGHTDM_LIBRARIES} + PRIVATE ${WINTC_COMCTL_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_WINBRAND_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() +wintc_add_pkgconfig_install() + +install( + TARGETS libwintc-msgina + LIBRARY DESTINATION ${LIB_DIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) diff --git a/shared/msgina/README.MD b/shared/msgina/README.MD new file mode 100644 index 0000000..c30a24b --- /dev/null +++ b/shared/msgina/README.MD @@ -0,0 +1,7 @@ +# libwintc-msgina +This directory contains the source-code for the GINA services (logon/security/auth related functions). + +# Purpose +This library provides services related to security. At the moment this includes the APIs for performing user logon (via LightDM), and contains the 'classic' logon dialog. + +In future this should be expanded to handle other forms of logon rather than just simple text based local auth, as well as things like the Windows Security dialog. diff --git a/shared/msgina/deps b/shared/msgina/deps new file mode 100644 index 0000000..bc757c0 --- /dev/null +++ b/shared/msgina/deps @@ -0,0 +1,4 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:wintc-comgtk +bt,rt:wintc-winbrand diff --git a/shared/msgina/public/wintc-msgina.h b/shared/msgina/public/wintc-msgina.h new file mode 100644 index 0000000..f74416b --- /dev/null +++ b/shared/msgina/public/wintc-msgina.h @@ -0,0 +1,77 @@ +#ifndef __WINTC_MSGINA_H__ +#define __WINTC_MSGINA_H__ + +#include +#include + +// +// Authentication window +// +typedef struct _WinTCGinaAuthWindowPrivate WinTCGinaAuthWindowPrivate; +typedef struct _WinTCGinaAuthWindowClass WinTCGinaAuthWindowClass; +typedef struct _WinTCGinaAuthWindow WinTCGinaAuthWindow; + +#define TYPE_WINTC_GINA_AUTH_WINDOW (wintc_gina_auth_window_get_type()) +#define WINTC_GINA_AUTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_GINA_AUTH_WINDOW, WinTCGinaAuthWindow)) +#define WINTC_GINA_AUTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_GINA_AUTH_WINDOW, WinTCGinaAuthWindow)) +#define IS_WINTC_GINA_AUTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_GINA_AUTH_WINDOW)) +#define IS_WINTC_GINA_AUTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_GINA_AUTH_WINDOW)) +#define WINTC_GINA_WINDOW_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_GINA_AUTH_WINDOW)) + +GType wintc_gina_auth_window_get_type(void) G_GNUC_CONST; + +GtkWidget* wintc_gina_auth_window_new(void); + +// +// Challenge auth +// +typedef enum +{ + WINTC_GINA_RESPONSE_OKAY = 0, + WINTC_GINA_RESPONSE_FAIL = 1 +} WinTCGinaResponse; + +// +// Logon session +// +typedef struct _WinTCGinaLogonSessionClass WinTCGinaLogonSessionClass; +typedef struct _WinTCGinaLogonSession WinTCGinaLogonSession; + +#define TYPE_WINTC_GINA_LOGON_SESSION (wintc_gina_logon_session_get_type()) +#define WINTC_GINA_LOGON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_GINA_LOGON_SESSION, WinTCGinaLogonSession)) +#define WINTC_GINA_LOGON_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_GINA_LOGON_SESSION, WinTCGinaLogonSession)) +#define IS_WINTC_GINA_LOGON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_GINA_LOGON_SESSION)) +#define IS_WINTC_GINA_LOGON_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_GINA_LOGON_SESSION)) +#define WINTC_GINA_LOGON_SESSION_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_GINA_LOGON_SESSION) + +GType wintc_gina_logon_session_get_type(void) G_GNUC_CONST; + +WinTCGinaLogonSession* wintc_gina_logon_session_new(void); + +gboolean wintc_gina_logon_session_establish( + WinTCGinaLogonSession* logon_session +); +void wintc_gina_logon_session_finish( + WinTCGinaLogonSession* logon_session +); +gboolean wintc_gina_logon_session_is_available( + WinTCGinaLogonSession* logon_session +); +void wintc_gina_logon_session_try_logon( + WinTCGinaLogonSession* logon_session, + const gchar* username, + const gchar* password +); + +// +// State +// +typedef enum +{ + WINTC_GINA_STATE_NONE = 0, + WINTC_GINA_STATE_STARTING, + WINTC_GINA_STATE_PROMPT, + WINTC_GINA_STATE_LAUNCHING +} WinTCGinaState; + +#endif \ No newline at end of file diff --git a/shared/msgina/src/authwnd.c b/shared/msgina/src/authwnd.c new file mode 100644 index 0000000..3916ec5 --- /dev/null +++ b/shared/msgina/src/authwnd.c @@ -0,0 +1,631 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "authwnd.h" +#include "challenge.h" +#include "logon.h" +#include "state.h" +#include "stripctl.h" + +#define DELAY_SECONDS_AT_LEAST 2 +#define DELAY_SECONDS_POLL 1 + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCGinaAuthWindowPrivate +{ + // Image resources + // + GdkPixbuf* pixbuf_banner; + GdkPixbuf* pixbuf_bannerx; + + // UI + // + GtkWidget* box_container; + + GtkWidget* box_brand; + GtkWidget* box_login; + GtkWidget* box_wait; + + GtkWidget* button_submit; + GtkWidget* entry_password; + GtkWidget* entry_username; + + GtkWidget* strip; + + // State + // + WinTCGinaState current_state; + WinTCGinaLogonSession* logon_session; +}; + +struct _WinTCGinaAuthWindowClass +{ + GtkWindowClass __parent__; +}; + +struct _WinTCGinaAuthWindow +{ + GtkWindow __parent__; + + WinTCGinaAuthWindowPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_gina_auth_window_finalize( + GObject* object +); + +static void wintc_gina_auth_window_change_state( + WinTCGinaAuthWindow* window, + WinTCGinaState next_state +); + +static void on_self_realized( + GtkWidget* self, + gpointer user_data +); + +static void on_logon_session_attempt_complete( + WinTCGinaLogonSession* logon_session, + WinTCGinaResponse response, + gpointer user_data +); + +static void on_button_submit_clicked( + GtkButton* self, + gpointer user_data +); + +static gboolean on_timeout_delay_done( + gpointer user_data +); +static gboolean on_timeout_poll_ready( + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCGinaAuthWindow, + wintc_gina_auth_window, + GTK_TYPE_WINDOW, + G_ADD_PRIVATE(WinTCGinaAuthWindow) +) + +static void wintc_gina_auth_window_class_init( + WinTCGinaAuthWindowClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_gina_auth_window_finalize; + + // Load up styles + // + GtkCssProvider* css_authwnd = gtk_css_provider_new(); + + gtk_css_provider_load_from_resource( + css_authwnd, + "/uk/oddmatics/wintc/msgina/authwnd.css" + ); + + gtk_style_context_add_provider_for_screen( + gdk_screen_get_default(), + GTK_STYLE_PROVIDER(css_authwnd), + GTK_STYLE_PROVIDER_PRIORITY_FALLBACK + ); + + wintc_comctl_install_default_styles(); +} + +static void wintc_gina_auth_window_init( + WinTCGinaAuthWindow* self +) +{ + self->priv = wintc_gina_auth_window_get_instance_private(self); + + // Acquire branding pixbufs + // + GError* err_banner = NULL; + GError* err_bannerx = NULL; + + self->priv->pixbuf_banner = wintc_brand_get_brand_pixmap( + WINTC_BRAND_PART_BANNER, + &err_banner + ); + self->priv->pixbuf_bannerx = wintc_brand_get_brand_pixmap( + WINTC_BRAND_PART_BANNER_TALL, + &err_bannerx + ); + + wintc_display_error_and_clear(&err_banner); + wintc_display_error_and_clear(&err_bannerx); + + // Set up branding box + // + GtkWidget* image_brand = + gtk_image_new_from_pixbuf( + self->priv->pixbuf_bannerx + ); + + self->priv->box_brand = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + self->priv->strip = wintc_gina_strip_new(); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_brand), + image_brand, + FALSE, + FALSE, + 0 + ); + gtk_box_pack_start( + GTK_BOX(self->priv->box_brand), + self->priv->strip, + FALSE, + FALSE, + 0 + ); + + // Set up 'Please wait...' box + // + GtkWidget* label_wait = gtk_label_new("Please wait..."); + + self->priv->box_wait = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + wintc_widget_add_style_class( + self->priv->box_wait, + "please-wait" + ); + + gtk_widget_set_halign( + label_wait, + GTK_ALIGN_START + ); + gtk_widget_set_valign( + label_wait, + GTK_ALIGN_START + ); + + gtk_box_pack_start( + GTK_BOX(self->priv->box_wait), + label_wait, + TRUE, + TRUE, + 0 + ); + + // Set up login box + // + GtkWidget* box_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + GtkWidget* grid_login = gtk_grid_new(); + GtkWidget* label_password = gtk_label_new("Password:"); + GtkWidget* label_username = gtk_label_new("User name:"); + + self->priv->box_login = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + self->priv->button_submit = gtk_button_new_with_label("OK"); + self->priv->entry_password = gtk_entry_new(); + self->priv->entry_username = gtk_entry_new(); + + wintc_widget_add_style_class( + box_buttons, + WINTC_COMCTL_BUTTON_BOX_CSS_CLASS + ); + + wintc_widget_add_style_class( + self->priv->box_login, + "login" + ); + + gtk_widget_set_halign( + label_password, + GTK_ALIGN_START + ); + gtk_widget_set_halign( + label_username, + GTK_ALIGN_START + ); + + gtk_entry_set_visibility(GTK_ENTRY(self->priv->entry_password), FALSE); + + g_signal_connect( + self->priv->button_submit, + "clicked", + G_CALLBACK(on_button_submit_clicked), + self + ); + + gtk_grid_attach( + GTK_GRID(grid_login), + label_username, + 0, + 0, + 1, + 1 + ); + gtk_grid_attach( + GTK_GRID(grid_login), + self->priv->entry_username, + 1, + 0, + 1, + 1 + ); + + gtk_grid_attach( + GTK_GRID(grid_login), + label_password, + 0, + 1, + 1, + 1 + ); + gtk_grid_attach( + GTK_GRID(grid_login), + self->priv->entry_password, + 1, + 1, + 1, + 1 + ); + + gtk_box_pack_end( + GTK_BOX(box_buttons), + self->priv->button_submit, + FALSE, + FALSE, + 0 + ); + + gtk_container_add( + GTK_CONTAINER(self->priv->box_login), + grid_login + ); + gtk_container_add( + GTK_CONTAINER(self->priv->box_login), + box_buttons + ); + + // Set up remainder of UI + // + GtkWidget* header_bar = gtk_header_bar_new(); + + self->priv->box_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + + gtk_header_bar_set_title( + GTK_HEADER_BAR(header_bar), + "Log On to Windows" + ); + + gtk_container_add( + GTK_CONTAINER(self->priv->box_container), + self->priv->box_brand + ); + + gtk_container_add( + GTK_CONTAINER(self), + self->priv->box_container + ); + + gtk_window_set_titlebar( + GTK_WINDOW(self), + header_bar + ); + + // Hold additional references to the boxes, so we can add/remove + // them ourselves without them getting binned + // + g_object_ref(self->priv->box_login); + g_object_ref(self->priv->box_wait); + + // Connect to realize signal to kick off everything when we're + // actually live + // + g_signal_connect( + self, + "realize", + G_CALLBACK(on_self_realized), + NULL + ); + + // Set initial state + // + self->priv->current_state = WINTC_GINA_STATE_NONE; + self->priv->logon_session = wintc_gina_logon_session_new(); + + g_signal_connect( + self->priv->logon_session, + "attempt-complete", + G_CALLBACK(on_logon_session_attempt_complete), + self + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_gina_auth_window_finalize( + GObject* gobject +) +{ + WinTCGinaAuthWindow* window = WINTC_GINA_AUTH_WINDOW(gobject); + + g_clear_object(&(window->priv->pixbuf_banner)); + g_clear_object(&(window->priv->pixbuf_bannerx)); + + // Bin additional references held for the boxes + // + g_clear_object(&(window->priv->box_login)); + g_clear_object(&(window->priv->box_wait)); + + (G_OBJECT_CLASS(wintc_gina_auth_window_parent_class))->finalize(gobject); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_auth_window_new(void) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_GINA_AUTH_WINDOW, + "type", GTK_WINDOW_TOPLEVEL, + "resizable", FALSE, + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_gina_auth_window_change_state( + WinTCGinaAuthWindow* window, + WinTCGinaState next_state +) +{ + // Disable current state, if any + // + switch (window->priv->current_state) + { + case WINTC_GINA_STATE_STARTING: + case WINTC_GINA_STATE_LAUNCHING: + wintc_gina_strip_stop_animating( + WINTC_GINA_STRIP(window->priv->strip) + ); + gtk_container_remove( + GTK_CONTAINER(window->priv->box_container), + window->priv->box_wait + ); + break; + + case WINTC_GINA_STATE_PROMPT: + gtk_container_remove( + GTK_CONTAINER(window->priv->box_container), + window->priv->box_login + ); + break; + + default: break; + } + + // Set up new state + // + switch (next_state) + { + case WINTC_GINA_STATE_STARTING: + gtk_box_pack_start( + GTK_BOX(window->priv->box_container), + window->priv->box_wait, + TRUE, + TRUE, + 0 + ); + wintc_gina_strip_animate( + WINTC_GINA_STRIP(window->priv->strip) + ); + wintc_gina_logon_session_establish( + window->priv->logon_session + ); + g_timeout_add_seconds( + DELAY_SECONDS_AT_LEAST, + on_timeout_delay_done, + window + ); + break; + + case WINTC_GINA_STATE_PROMPT: + gtk_box_pack_start( + GTK_BOX(window->priv->box_container), + window->priv->box_login, + TRUE, + TRUE, + 0 + ); + break; + + case WINTC_GINA_STATE_LAUNCHING: + gtk_box_pack_start( + GTK_BOX(window->priv->box_container), + window->priv->box_wait, + TRUE, + TRUE, + 0 + ); + wintc_gina_strip_animate( + WINTC_GINA_STRIP(window->priv->strip) + ); + g_timeout_add_seconds( + DELAY_SECONDS_AT_LEAST, + on_timeout_delay_done, + window + ); + break; + + default: break; + } + + gtk_widget_show_all( + window->priv->box_container + ); + + window->priv->current_state = next_state; +} + +// +// CALLBACKS +// +static void on_self_realized( + GtkWidget* self, + WINTC_UNUSED(gpointer user_data) +) +{ + wintc_gina_auth_window_change_state( + WINTC_GINA_AUTH_WINDOW(self), + WINTC_GINA_STATE_STARTING + ); +} + +static void on_logon_session_attempt_complete( + WINTC_UNUSED(WinTCGinaLogonSession* logon_session), + WinTCGinaResponse response, + gpointer user_data +) +{ + WinTCGinaAuthWindow* window = WINTC_GINA_AUTH_WINDOW(user_data); + + switch (response) + { + case WINTC_GINA_RESPONSE_OKAY: + wintc_gina_auth_window_change_state( + window, + WINTC_GINA_STATE_LAUNCHING + ); + break; + + case WINTC_GINA_RESPONSE_FAIL: + // FIXME: Prompt for failure + gtk_entry_set_text( + GTK_ENTRY(window->priv->entry_password), + "" + ); + gtk_entry_set_text( + GTK_ENTRY(window->priv->entry_username), + "" + ); + + gtk_widget_set_sensitive( + window->priv->button_submit, + TRUE + ); + gtk_widget_set_sensitive( + window->priv->entry_password, + TRUE + ); + gtk_widget_set_sensitive( + window->priv->entry_username, + TRUE + ); + + wintc_gina_strip_stop_animating( + WINTC_GINA_STRIP(window->priv->strip) + ); + + break; + + default: break; + } +} + +static void on_button_submit_clicked( + WINTC_UNUSED(GtkButton* self), + gpointer user_data +) +{ + WinTCGinaAuthWindow* window = WINTC_GINA_AUTH_WINDOW(user_data); + + gtk_widget_set_sensitive( + window->priv->button_submit, + FALSE + ); + gtk_widget_set_sensitive( + window->priv->entry_password, + FALSE + ); + gtk_widget_set_sensitive( + window->priv->entry_username, + FALSE + ); + + wintc_gina_strip_animate( + WINTC_GINA_STRIP(window->priv->strip) + ); + + wintc_gina_logon_session_try_logon( + window->priv->logon_session, + gtk_entry_get_text(GTK_ENTRY(window->priv->entry_username)), + gtk_entry_get_text(GTK_ENTRY(window->priv->entry_password)) + ); +} + +static gboolean on_timeout_delay_done( + gpointer user_data +) +{ + WinTCGinaAuthWindow* window = WINTC_GINA_AUTH_WINDOW(user_data); + + switch (window->priv->current_state) + { + case WINTC_GINA_STATE_STARTING: + g_timeout_add_seconds( + DELAY_SECONDS_POLL, + on_timeout_poll_ready, + window + ); + break; + + case WINTC_GINA_STATE_LAUNCHING: + wintc_gina_logon_session_finish( + window->priv->logon_session + ); + break; + + default: + g_critical("%s", "Invalid state reached for delay."); + break; + } + + return G_SOURCE_REMOVE; +} + +static gboolean on_timeout_poll_ready( + gpointer user_data +) +{ + WinTCGinaAuthWindow* window = WINTC_GINA_AUTH_WINDOW(user_data); + + if ( + wintc_gina_logon_session_is_available( + window->priv->logon_session + ) + ) + { + wintc_gina_auth_window_change_state( + window, + WINTC_GINA_STATE_PROMPT + ); + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} \ No newline at end of file diff --git a/shared/msgina/src/authwnd.h b/shared/msgina/src/authwnd.h new file mode 100644 index 0000000..c2c5a80 --- /dev/null +++ b/shared/msgina/src/authwnd.h @@ -0,0 +1,28 @@ +#ifndef __AUTHWND_H__ +#define __AUTHWND_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCGinaAuthWindowPrivate WinTCGinaAuthWindowPrivate; +typedef struct _WinTCGinaAuthWindowClass WinTCGinaAuthWindowClass; +typedef struct _WinTCGinaAuthWindow WinTCGinaAuthWindow; + +#define TYPE_WINTC_GINA_AUTH_WINDOW (wintc_gina_auth_window_get_type()) +#define WINTC_GINA_AUTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_GINA_AUTH_WINDOW, WinTCGinaAuthWindow)) +#define WINTC_GINA_AUTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_GINA_AUTH_WINDOW, WinTCGinaAuthWindow)) +#define IS_WINTC_GINA_AUTH_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_GINA_AUTH_WINDOW)) +#define IS_WINTC_GINA_AUTH_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_GINA_AUTH_WINDOW)) +#define WINTC_GINA_WINDOW_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_GINA_AUTH_WINDOW)) + +GType wintc_gina_auth_window_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_auth_window_new(void); + +#endif diff --git a/shared/msgina/src/challenge.h b/shared/msgina/src/challenge.h new file mode 100644 index 0000000..7ef349b --- /dev/null +++ b/shared/msgina/src/challenge.h @@ -0,0 +1,10 @@ +#ifndef __CHALLENGE_H__ +#define __CHALLENGE_H__ + +typedef enum +{ + WINTC_GINA_RESPONSE_OKAY = 0, + WINTC_GINA_RESPONSE_FAIL = 1 +} WinTCGinaResponse; + +#endif \ No newline at end of file diff --git a/shared/msgina/src/logon.c b/shared/msgina/src/logon.c new file mode 100644 index 0000000..751d9b6 --- /dev/null +++ b/shared/msgina/src/logon.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include "challenge.h" +#include "logon.h" + +// +// PRIVATE ENUMS +// +enum +{ + SIGNAL_ATTEMPT_COMPLETE = 0, + N_SIGNALS +}; + +// +// STATIC DATA +// +static gint wintc_gina_logon_session_signals[N_SIGNALS] = { 0 }; + +// +// GLIB OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCGinaLogonSessionClass +{ + GObjectClass __parent__; +}; + +struct _WinTCGinaLogonSession +{ + GObject __parent__; + + gboolean auth_complete; + gboolean auth_ready; + LightDMGreeter* greeter; + gchar* response_pwd; +}; + +// +// FORWARD DECLARATIONS +// +static void on_greeter_authentication_complete( + LightDMGreeter* greeter, + gpointer user_data +); + +static void on_greeter_show_prompt( + LightDMGreeter* greeter, + gchar* text, + WINTC_UNUSED(LightDMPromptType type), + gpointer user_data +); + +// +// GLIB TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCGinaLogonSession, + wintc_gina_logon_session, + G_TYPE_OBJECT +) + +static void wintc_gina_logon_session_class_init( + WinTCGinaLogonSessionClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + wintc_gina_logon_session_signals[SIGNAL_ATTEMPT_COMPLETE] = + g_signal_new( + "attempt-complete", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT + ); +} + +static void wintc_gina_logon_session_init( + WinTCGinaLogonSession* self +) +{ + self->auth_ready = FALSE; + self->greeter = NULL; +} + +// +// PUBLIC FUNCTIONS +// +WinTCGinaLogonSession* wintc_gina_logon_session_new() +{ + return WINTC_GINA_LOGON_SESSION( + g_object_new( + TYPE_WINTC_GINA_LOGON_SESSION, + NULL + ) + ); +} + +gboolean wintc_gina_logon_session_establish( + WinTCGinaLogonSession* logon_session +) +{ + WINTC_LOG_DEBUG("GINA - establish()"); + + if (!logon_session->greeter) + { + WINTC_LOG_DEBUG("GINA - new lightdm greeter"); + + logon_session->greeter = lightdm_greeter_new(); + + g_signal_connect( + logon_session->greeter, + "authentication-complete", + G_CALLBACK(on_greeter_authentication_complete), + logon_session + ); + g_signal_connect( + logon_session->greeter, + "show-prompt", + G_CALLBACK(on_greeter_show_prompt), + logon_session + ); + } + + WINTC_LOG_DEBUG("GINA - lightdm connect sync"); + + if ( + !lightdm_greeter_connect_to_daemon_sync( + logon_session->greeter, + NULL + ) + ) + { + g_critical("%s", "Failed to connect to LightDM daemon."); + return FALSE; + } + + // FIXME: Error handling + // + lightdm_greeter_authenticate( + logon_session->greeter, + NULL, + NULL + ); + + return TRUE; +} + +void wintc_gina_logon_session_finish( + WinTCGinaLogonSession* logon_session +) +{ + WINTC_LOG_DEBUG("GINA - finish requested"); + + if (!logon_session->auth_complete) + { + g_warning("%s", "Attempt to complete incomplete logon."); + return; + } + + // FIXME: Error handling required + // FIXME: If this fails, we need to restart auth! The UI + // does not handle this either + // + lightdm_greeter_start_session_sync( + logon_session->greeter, + NULL, + NULL + ); +} + +gboolean wintc_gina_logon_session_is_available( + WinTCGinaLogonSession* logon_session +) +{ + WINTC_LOG_DEBUG("GINA - polled"); + + return logon_session->auth_ready; +} + +void wintc_gina_logon_session_try_logon( + WinTCGinaLogonSession* logon_session, + const gchar* username, + const gchar* password +) +{ + WINTC_LOG_DEBUG("GINA - logon attempt requested"); + + if (logon_session->auth_complete) + { + g_warning("%s", "Attempt to logon a second time."); + return; + } + + logon_session->response_pwd = g_strdup(password); + + // FIXME: Error handling + // + lightdm_greeter_respond( + logon_session->greeter, + username, + NULL + ); +} + +// +// CALLBACKS +// +static void on_greeter_authentication_complete( + LightDMGreeter* greeter, + gpointer user_data +) +{ + WinTCGinaLogonSession* logon_session = + WINTC_GINA_LOGON_SESSION(user_data); + + WINTC_LOG_DEBUG("GINA - lightdm auth complete"); + + logon_session->auth_complete = + lightdm_greeter_get_is_authenticated(greeter); + + g_signal_emit( + logon_session, + wintc_gina_logon_session_signals[SIGNAL_ATTEMPT_COMPLETE], + 0, + logon_session->auth_complete ? + WINTC_GINA_RESPONSE_OKAY : + WINTC_GINA_RESPONSE_FAIL + ); + + // If the auth failed, have to restart + // + if (!logon_session->auth_complete) + { + // FIXME: Error handling + // + lightdm_greeter_authenticate(greeter, NULL, NULL); + } +} + +static void on_greeter_show_prompt( + LightDMGreeter* greeter, + gchar* text, + WINTC_UNUSED(LightDMPromptType type), + gpointer user_data +) +{ + WinTCGinaLogonSession* logon_session = + WINTC_GINA_LOGON_SESSION(user_data); + + WINTC_LOG_DEBUG("GINA - lightdm prompt: %s", text); + + if (g_strcmp0(text, "login:") == 0) + { + logon_session->auth_ready = TRUE; + } + else if (g_strcmp0(text, "Password: ") == 0) + { + // FIXME: Error handling + // + lightdm_greeter_respond( + greeter, + logon_session->response_pwd, + NULL + ); + + g_free(logon_session->response_pwd); + logon_session->response_pwd = NULL; + } + else + { + g_critical("Unknown prompt: %s", text); + } +} \ No newline at end of file diff --git a/shared/msgina/src/logon.h b/shared/msgina/src/logon.h new file mode 100644 index 0000000..4ca1bfc --- /dev/null +++ b/shared/msgina/src/logon.h @@ -0,0 +1,41 @@ +#ifndef __LOGON_H__ +#define __LOGON_H__ + +#include + +// +// GLIB BOILERPLATE +// +typedef struct _WinTCGinaLogonSessionClass WinTCGinaLogonSessionClass; +typedef struct _WinTCGinaLogonSession WinTCGinaLogonSession; + +#define TYPE_WINTC_GINA_LOGON_SESSION (wintc_gina_logon_session_get_type()) +#define WINTC_GINA_LOGON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_GINA_LOGON_SESSION, WinTCGinaLogonSession)) +#define WINTC_GINA_LOGON_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_GINA_LOGON_SESSION, WinTCGinaLogonSession)) +#define IS_WINTC_GINA_LOGON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_GINA_LOGON_SESSION)) +#define IS_WINTC_GINA_LOGON_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_GINA_LOGON_SESSION)) +#define WINTC_GINA_LOGON_SESSION_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_GINA_LOGON_SESSION) + +GType wintc_gina_logon_session_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +WinTCGinaLogonSession* wintc_gina_logon_session_new(void); + +gboolean wintc_gina_logon_session_establish( + WinTCGinaLogonSession* logon_session +); +void wintc_gina_logon_session_finish( + WinTCGinaLogonSession* logon_session +); +gboolean wintc_gina_logon_session_is_available( + WinTCGinaLogonSession* logon_session +); +void wintc_gina_logon_session_try_logon( + WinTCGinaLogonSession* logon_session, + const gchar* username, + const gchar* password +); + +#endif \ No newline at end of file diff --git a/shared/msgina/src/res/authwnd.css b/shared/msgina/src/res/authwnd.css new file mode 100644 index 0000000..e90c295 --- /dev/null +++ b/shared/msgina/src/res/authwnd.css @@ -0,0 +1,20 @@ +box.please-wait +{ + margin: 15px 0px; + margin-left: 8px; +} + +grid +{ + margin: 15px 0px; +} + +grid * +{ + margin: 3px 10px 4px; +} + +grid entry +{ + min-width: 240px; +} \ No newline at end of file diff --git a/shared/msgina/src/res/resources.xml b/shared/msgina/src/res/resources.xml new file mode 100644 index 0000000..14581cc --- /dev/null +++ b/shared/msgina/src/res/resources.xml @@ -0,0 +1,6 @@ + + + + authwnd.css + + \ No newline at end of file diff --git a/shared/msgina/src/state.h b/shared/msgina/src/state.h new file mode 100644 index 0000000..087bd3d --- /dev/null +++ b/shared/msgina/src/state.h @@ -0,0 +1,12 @@ +#ifndef __STATE_H__ +#define __STATE_H__ + +typedef enum +{ + WINTC_GINA_STATE_NONE = 0, + WINTC_GINA_STATE_STARTING, + WINTC_GINA_STATE_PROMPT, + WINTC_GINA_STATE_LAUNCHING +} WinTCGinaState; + +#endif \ No newline at end of file diff --git a/shared/msgina/src/stripctl.c b/shared/msgina/src/stripctl.c new file mode 100644 index 0000000..bcf3ac5 --- /dev/null +++ b/shared/msgina/src/stripctl.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include + +#include "stripctl.h" + +#define ONE_SECOND_IN_US 1000000 + +#define ANIM_RATE_SECONDS 2 +#define ANIM_RATE_IN_US (ANIM_RATE_SECONDS * ONE_SECOND_IN_US) + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCGinaStripPrivate +{ + GdkPixbuf* pixbuf_strip; + cairo_surface_t* surface_strip; + + gboolean is_animating; + gint64 origin_time; +}; + +struct _WinTCGinaStripClass +{ + GtkWidgetClass __parent__; +}; + +struct _WinTCGinaStrip +{ + GtkWidget __parent__; + + WinTCGinaStripPrivate* priv; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_gina_strip_finalize( + GObject* gobject +); + +static gboolean wintc_gina_strip_draw( + GtkWidget* widget, + cairo_t* cr +); +static void wintc_gina_strip_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +); +static void wintc_gina_strip_get_preferred_height_for_width( + GtkWidget* widget, + gint width, + gint* minimum_height, + gint* natural_height +); +static void wintc_gina_strip_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +); +static void wintc_gina_strip_get_preferred_width_for_height( + GtkWidget* widget, + gint height, + gint* minimum_width, + gint* natural_width +); + +static gboolean wintc_gina_strip_step( + GtkWidget* widget, + GdkFrameClock* frame_clock, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE_WITH_CODE( + WinTCGinaStrip, + wintc_gina_strip, + GTK_TYPE_WIDGET, + G_ADD_PRIVATE(WinTCGinaStrip) +) + +static void wintc_gina_strip_class_init( + WinTCGinaStripClass* klass +) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_gina_strip_finalize; + + widget_class->draw = + wintc_gina_strip_draw; + widget_class->get_preferred_height = + wintc_gina_strip_get_preferred_height; + widget_class->get_preferred_height_for_width = + wintc_gina_strip_get_preferred_height_for_width; + widget_class->get_preferred_width = + wintc_gina_strip_get_preferred_width; + widget_class->get_preferred_width_for_height = + wintc_gina_strip_get_preferred_width_for_height; +} + +static void wintc_gina_strip_init( + WinTCGinaStrip* self +) +{ + self->priv = wintc_gina_strip_get_instance_private(self); + + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); + + // Load assets + // + GError* error = NULL; + + self->priv->pixbuf_strip = + wintc_brand_get_brand_pixmap( + WINTC_BRAND_PART_STRIP_ANIM, + &error + ); + + // FIXME: Handle error + + self->priv->surface_strip = + gdk_cairo_surface_create_from_pixbuf( + self->priv->pixbuf_strip, + 1, + NULL // FIXME: Error reporting + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_gina_strip_finalize( + GObject* gobject +) +{ + WinTCGinaStrip* strip = WINTC_GINA_STRIP(gobject); + + cairo_surface_destroy(strip->priv->surface_strip); + g_clear_object(&(strip->priv->pixbuf_strip)); + + (G_OBJECT_CLASS(wintc_gina_strip_parent_class))->finalize(gobject); +} + +static gboolean wintc_gina_strip_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCGinaStrip* strip = WINTC_GINA_STRIP(widget); + gdouble x_offset = 0.0f; + + // If animating, work out offset of strip based + // on time + // + if (strip->priv->is_animating) + { + gint64 current_time = g_get_monotonic_time(); + gint64 delta = strip->priv->origin_time - current_time; + gint64 mod_time = delta % ANIM_RATE_IN_US; + gdouble within_pct = (gdouble) mod_time / ANIM_RATE_IN_US; + + gint img_width = + cairo_image_surface_get_width( + strip->priv->surface_strip + ); + + x_offset = (gint) (img_width * within_pct); + } + + // Paint now + // + cairo_set_source_surface( + cr, + strip->priv->surface_strip, + x_offset, + 0.0f + ); + cairo_pattern_set_extend( + cairo_get_source(cr), + CAIRO_EXTEND_REPEAT + ); + + cairo_paint(cr); + + return FALSE; +} + +static void wintc_gina_strip_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +) +{ + gint height; + WinTCGinaStrip* strip = WINTC_GINA_STRIP(widget); + + height = + cairo_image_surface_get_height( + strip->priv->surface_strip + ); + + *minimum_height = height; + *natural_height = height; +} + +static void wintc_gina_strip_get_preferred_height_for_width( + GtkWidget* widget, + WINTC_UNUSED(gint width), + gint* minimum_height, + gint* natural_height +) +{ + wintc_gina_strip_get_preferred_height( + widget, + minimum_height, + natural_height + ); +} + +static void wintc_gina_strip_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +) +{ + WinTCGinaStrip* strip = WINTC_GINA_STRIP(widget); + gint width; + + width = + cairo_image_surface_get_width( + strip->priv->surface_strip + ); + + *minimum_width = width; + *natural_width = width; +} + +static void wintc_gina_strip_get_preferred_width_for_height( + GtkWidget* widget, + WINTC_UNUSED(gint height), + gint* minimum_width, + gint* natural_width +) +{ + wintc_gina_strip_get_preferred_width( + widget, + minimum_width, + natural_width + ); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_strip_new(void) +{ + return GTK_WIDGET( + g_object_new( + TYPE_WINTC_GINA_STRIP, + NULL + ) + ); +} + +void wintc_gina_strip_animate( + WinTCGinaStrip* strip +) +{ + if (strip->priv->is_animating) + { + return; + } + + strip->priv->is_animating = TRUE; + strip->priv->origin_time = g_get_monotonic_time(); + + gtk_widget_add_tick_callback( + GTK_WIDGET(strip), + (GtkTickCallback) wintc_gina_strip_step, + NULL, + NULL + ); +} + +void wintc_gina_strip_stop_animating( + WinTCGinaStrip* strip +) +{ + strip->priv->is_animating = FALSE; +} + +// +// CALLBACKS +// +static gboolean wintc_gina_strip_step( + GtkWidget* widget, + WINTC_UNUSED(GdkFrameClock* frame_clock), + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCGinaStrip* strip = WINTC_GINA_STRIP(widget); + + gtk_widget_queue_draw(widget); + + return strip->priv->is_animating ? + G_SOURCE_CONTINUE : G_SOURCE_REMOVE; +} \ No newline at end of file diff --git a/shared/msgina/src/stripctl.h b/shared/msgina/src/stripctl.h new file mode 100644 index 0000000..ca9ef9c --- /dev/null +++ b/shared/msgina/src/stripctl.h @@ -0,0 +1,32 @@ +#ifndef __STRIPCTL_H__ +#define __STRIPCTL_H__ + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCGinaStripPrivate WinTCGinaStripPrivate; +typedef struct _WinTCGinaStripClass WinTCGinaStripClass; +typedef struct _WinTCGinaStrip WinTCGinaStrip; + +#define TYPE_WINTC_GINA_STRIP (wintc_gina_strip_get_type()) +#define WINTC_GINA_STRIP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_WINTC_GINA_STRIP, WinTCGinaStrip)) +#define WINTC_GINA_STRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_WINTC_GINA_STRIP, WinTCGinaStrip)) +#define IS_WINTC_GINA_STRIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_WINTC_GINA_STRIP)) +#define IS_WINTC_GINA_STRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_WINTC_GINA_STRIP)) +#define WINTC_GINA_STRIP_GET_CLASS(obj) (G_TYPE_CHECK_INSTANCE_GET_CLASS((obj), TYPE_WINTC_GINA_STRIP)) + +GType wintc_gina_strip_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_gina_strip_new(void); + +void wintc_gina_strip_animate( + WinTCGinaStrip* strip +); +void wintc_gina_strip_stop_animating( + WinTCGinaStrip* strip +); + +#endif \ No newline at end of file diff --git a/shared/shelldpa/src/dll/wnck.c b/shared/shelldpa/src/dll/wnck.c index f2abc43..4e41e3b 100644 --- a/shared/shelldpa/src/dll/wnck.c +++ b/shared/shelldpa/src/dll/wnck.c @@ -108,7 +108,8 @@ gboolean init_dll_wnck() p_wnck_window_get_mini_icon == NULL || p_wnck_window_get_name == NULL || p_wnck_window_is_skip_tasklist == NULL || - p_wnck_window_minimize == NULL + p_wnck_window_minimize == NULL || + p_wnck_window_unminimize == NULL ) { g_warning("%s", "libwnck loaded, but not all symbols."); diff --git a/shared/winbrand/CMakeLists.txt b/shared/winbrand/CMakeLists.txt index 58c8293..9502df6 100644 --- a/shared/winbrand/CMakeLists.txt +++ b/shared/winbrand/CMakeLists.txt @@ -20,6 +20,8 @@ include(../../packaging/cmake-inc/libraries/CMakeLists.txt) include(../../packaging/cmake-inc/linking/CMakeLists.txt) include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +set(WINTC_BRAND_DIR ${WINTC_ASSETS_INSTALL_DIR}/brand) + wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF) wintc_resolve_library(glib-2.0 GLIB) @@ -65,64 +67,50 @@ target_link_libraries( # Pick banner based on SKU # -if (${WINTC_SKU} MATCHES "^xpclient-(.+)") - if (${CMAKE_MATCH_1} STREQUAL "pro") - if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") - set(WINBRAND_BANNER_NAME x64pro) - elseif ( - ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ia64" OR - ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv8" - ) - set(WINBRAND_BANNER_NAME 64bit) - else() - set(WINBRAND_BANNER_NAME pro) - endif() - else() - set(WINBRAND_BANNER_NAME ${CMAKE_MATCH_1}) - endif() -elseif (${WINTC_SKU} MATCHES "^dnsrv-(.+)") - if ( - ${CMAKE_MATCH_1} STREQUAL "sbs" OR - ${CMAKE_MATCH_1} STREQUAL "web" OR - ${CMAKE_MATCH_1} STREQUAL "ccs" OR - NOT ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" - ) - set(WINBRAND_BANNER_NAME srv2k3${CMAKE_MATCH_1}) - else() - set(WINBRAND_BANNER_NAME x64srv2k3${CMAKE_MATCH_1}) - endif() -elseif (${WINTC_SKU} MATCHES "^dnsrv_r2-(.+)") +set(WINBRAND_BANNER_NAME ${WINTC_SKU}) + +if (${WINTC_SKU} STREQUAL "xpclient-pro") if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") - set(WINBRAND_BANNER_NAME x64srv2k3r2${CMAKE_MATCH_1}) - else() - set(WINBRAND_BANNER_NAME srv2k3r2${CMAKE_MATCH_1}) - endif() -elseif (${WINTC_SKU} STREQUAL "homesrv") - set(WINBRAND_BANNER_NAME homesrv) -else() - message( - FATAL_ERROR - "No banner defined for SKU: ${WINTC_SKU}" + set(WINBRAND_BANNER_NAME ${WINTC_SKU}_amd64) + elseif ( + ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ia64" OR + ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv8" ) + set(WINBRAND_BANNER_NAME ${WINTC_SKU}_ia64) + endif() +elseif (${WINTC_SKU} MATCHES "^dnsrv(_r2)?-(.+)") + if (${CMAKE_MATCH_2} MATCHES "(dtc|ent|std)") + set(WINBRAND_BANNER_NAME ${WINTC_SKU}_amd64) + endif() endif() + + # Installation # wintc_configure_and_install_packaging() wintc_add_pkgconfig_install() install( - FILES res/banner-${WINBRAND_BANNER_NAME}.png - DESTINATION ${WINTC_ASSETS_INSTALL_DIR}/brand + FILES res/banner/${WINBRAND_BANNER_NAME}.png + DESTINATION ${WINTC_BRAND_DIR} RENAME banner.png ) install( - FILES res/strip-xp.png - DESTINATION ${WINTC_ASSETS_INSTALL_DIR}/brand - RENAME strip.png + FILES res/bannerx/${WINBRAND_BANNER_NAME}.png + DESTINATION ${WINTC_BRAND_DIR} + RENAME bannerx.png +) +install( + FILES res/strip-static.png + DESTINATION ${WINTC_BRAND_DIR} +) +install( + FILES res/strip-anim.png + DESTINATION ${WINTC_BRAND_DIR} ) install( TARGETS libwintc-winbrand LIBRARY DESTINATION ${LIB_DIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) +) \ No newline at end of file diff --git a/shared/winbrand/public/wintc-winbrand.h b/shared/winbrand/public/wintc-winbrand.h index 192a978..24b4d5f 100644 --- a/shared/winbrand/public/wintc-winbrand.h +++ b/shared/winbrand/public/wintc-winbrand.h @@ -7,12 +7,17 @@ // // Branding banners // -GdkPixbuf* wintc_brand_get_banner( - GError** error -); +typedef enum +{ + WINTC_BRAND_PART_BANNER = 0, + WINTC_BRAND_PART_BANNER_TALL, + WINTC_BRAND_PART_STRIP_STATIC = 10, + WINTC_BRAND_PART_STRIP_ANIM +} WinTCBrandPart; -GdkPixbuf* wintc_brand_get_progress_strip( - GError** error +GdkPixbuf* wintc_brand_get_brand_pixmap( + WinTCBrandPart part, + GError** error ); #endif diff --git a/shared/winbrand/res/banner-dnsrvent.png b/shared/winbrand/res/banner/__dnsrv-ent.png similarity index 100% rename from shared/winbrand/res/banner-dnsrvent.png rename to shared/winbrand/res/banner/__dnsrv-ent.png diff --git a/shared/winbrand/res/banner-dnsrvstd.png b/shared/winbrand/res/banner/__dnsrv-std.png similarity index 100% rename from shared/winbrand/res/banner-dnsrvstd.png rename to shared/winbrand/res/banner/__dnsrv-std.png diff --git a/shared/winbrand/res/banner-dnsrv2k3ent.png b/shared/winbrand/res/banner/_dnsrv-ent.png similarity index 100% rename from shared/winbrand/res/banner-dnsrv2k3ent.png rename to shared/winbrand/res/banner/_dnsrv-ent.png diff --git a/shared/winbrand/res/banner-dnsrv2k3std.png b/shared/winbrand/res/banner/_dnsrv-std.png similarity index 100% rename from shared/winbrand/res/banner-dnsrv2k3std.png rename to shared/winbrand/res/banner/_dnsrv-std.png diff --git a/shared/winbrand/res/banner-srv2k3cce.png b/shared/winbrand/res/banner/dnsrv-ccs.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3cce.png rename to shared/winbrand/res/banner/dnsrv-ccs.png diff --git a/shared/winbrand/res/banner-srv2k3dtc.png b/shared/winbrand/res/banner/dnsrv-dtc.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3dtc.png rename to shared/winbrand/res/banner/dnsrv-dtc.png diff --git a/shared/winbrand/res/banner-x64srv2k3dtc.png b/shared/winbrand/res/banner/dnsrv-dtc_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3dtc.png rename to shared/winbrand/res/banner/dnsrv-dtc_amd64.png diff --git a/shared/winbrand/res/banner-srv2k3ent.png b/shared/winbrand/res/banner/dnsrv-ent.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3ent.png rename to shared/winbrand/res/banner/dnsrv-ent.png diff --git a/shared/winbrand/res/banner-x64srv2k3ent.png b/shared/winbrand/res/banner/dnsrv-ent_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3ent.png rename to shared/winbrand/res/banner/dnsrv-ent_amd64.png diff --git a/shared/winbrand/res/banner-srv2k3sbs.png b/shared/winbrand/res/banner/dnsrv-sbs.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3sbs.png rename to shared/winbrand/res/banner/dnsrv-sbs.png diff --git a/shared/winbrand/res/banner-srv2k3std.png b/shared/winbrand/res/banner/dnsrv-std.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3std.png rename to shared/winbrand/res/banner/dnsrv-std.png diff --git a/shared/winbrand/res/banner-x64srv2k3std.png b/shared/winbrand/res/banner/dnsrv-std_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3std.png rename to shared/winbrand/res/banner/dnsrv-std_amd64.png diff --git a/shared/winbrand/res/banner-srv2k3web.png b/shared/winbrand/res/banner/dnsrv-web.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3web.png rename to shared/winbrand/res/banner/dnsrv-web.png diff --git a/shared/winbrand/res/banner-srv2k3r2dtc.png b/shared/winbrand/res/banner/dnsrv_r2-dtc.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3r2dtc.png rename to shared/winbrand/res/banner/dnsrv_r2-dtc.png diff --git a/shared/winbrand/res/banner-x64srv2k3r2dtc.png b/shared/winbrand/res/banner/dnsrv_r2-dtc_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3r2dtc.png rename to shared/winbrand/res/banner/dnsrv_r2-dtc_amd64.png diff --git a/shared/winbrand/res/banner-srv2k3r2ent.png b/shared/winbrand/res/banner/dnsrv_r2-ent.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3r2ent.png rename to shared/winbrand/res/banner/dnsrv_r2-ent.png diff --git a/shared/winbrand/res/banner-x64srv2k3r2ent.png b/shared/winbrand/res/banner/dnsrv_r2-ent_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3r2ent.png rename to shared/winbrand/res/banner/dnsrv_r2-ent_amd64.png diff --git a/shared/winbrand/res/banner-srv2k3r2std.png b/shared/winbrand/res/banner/dnsrv_r2-std.png similarity index 100% rename from shared/winbrand/res/banner-srv2k3r2std.png rename to shared/winbrand/res/banner/dnsrv_r2-std.png diff --git a/shared/winbrand/res/banner-x64srv2k3r2std.png b/shared/winbrand/res/banner/dnsrv_r2-std_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64srv2k3r2std.png rename to shared/winbrand/res/banner/dnsrv_r2-std_amd64.png diff --git a/shared/winbrand/res/banner-homesrv.png b/shared/winbrand/res/banner/homesrv.png similarity index 100% rename from shared/winbrand/res/banner-homesrv.png rename to shared/winbrand/res/banner/homesrv.png diff --git a/shared/winbrand/res/banner-embedded.png b/shared/winbrand/res/banner/xpclient-embedded.png similarity index 100% rename from shared/winbrand/res/banner-embedded.png rename to shared/winbrand/res/banner/xpclient-embedded.png diff --git a/shared/winbrand/res/banner-flp.png b/shared/winbrand/res/banner/xpclient-flp.png similarity index 100% rename from shared/winbrand/res/banner-flp.png rename to shared/winbrand/res/banner/xpclient-flp.png diff --git a/shared/winbrand/res/banner-linux.png b/shared/winbrand/res/banner/xpclient-linux.png similarity index 100% rename from shared/winbrand/res/banner-linux.png rename to shared/winbrand/res/banner/xpclient-linux.png diff --git a/shared/winbrand/res/banner-mce.png b/shared/winbrand/res/banner/xpclient-mce.png similarity index 100% rename from shared/winbrand/res/banner-mce.png rename to shared/winbrand/res/banner/xpclient-mce.png diff --git a/shared/winbrand/res/banner-per.png b/shared/winbrand/res/banner/xpclient-per.png similarity index 100% rename from shared/winbrand/res/banner-per.png rename to shared/winbrand/res/banner/xpclient-per.png diff --git a/shared/winbrand/res/banner-posready.png b/shared/winbrand/res/banner/xpclient-posready.png similarity index 100% rename from shared/winbrand/res/banner-posready.png rename to shared/winbrand/res/banner/xpclient-posready.png diff --git a/shared/winbrand/res/banner-pro.png b/shared/winbrand/res/banner/xpclient-pro.png similarity index 100% rename from shared/winbrand/res/banner-pro.png rename to shared/winbrand/res/banner/xpclient-pro.png diff --git a/shared/winbrand/res/banner-x64pro.png b/shared/winbrand/res/banner/xpclient-pro_amd64.png similarity index 100% rename from shared/winbrand/res/banner-x64pro.png rename to shared/winbrand/res/banner/xpclient-pro_amd64.png diff --git a/shared/winbrand/res/banner-64bit.png b/shared/winbrand/res/banner/xpclient-pro_ia64.png similarity index 100% rename from shared/winbrand/res/banner-64bit.png rename to shared/winbrand/res/banner/xpclient-pro_ia64.png diff --git a/shared/winbrand/res/banner-starter.png b/shared/winbrand/res/banner/xpclient-starter.png similarity index 100% rename from shared/winbrand/res/banner-starter.png rename to shared/winbrand/res/banner/xpclient-starter.png diff --git a/shared/winbrand/res/banner-tabletpc.png b/shared/winbrand/res/banner/xpclient-tabletpc.png similarity index 100% rename from shared/winbrand/res/banner-tabletpc.png rename to shared/winbrand/res/banner/xpclient-tabletpc.png diff --git a/shared/winbrand/res/banner-wepos.png b/shared/winbrand/res/banner/xpclient-wepos.png similarity index 100% rename from shared/winbrand/res/banner-wepos.png rename to shared/winbrand/res/banner/xpclient-wepos.png diff --git a/shared/winbrand/res/banner-wes.png b/shared/winbrand/res/banner/xpclient-wes.png similarity index 100% rename from shared/winbrand/res/banner-wes.png rename to shared/winbrand/res/banner/xpclient-wes.png diff --git a/shared/winbrand/res/bannerx/dnsrv-ccs.png b/shared/winbrand/res/bannerx/dnsrv-ccs.png new file mode 100644 index 0000000..5755bf9 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-ccs.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-dtc.png b/shared/winbrand/res/bannerx/dnsrv-dtc.png new file mode 100644 index 0000000..ac86e54 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-dtc.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-dtc_amd64.png b/shared/winbrand/res/bannerx/dnsrv-dtc_amd64.png new file mode 100644 index 0000000..0661a66 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-dtc_amd64.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-ent.png b/shared/winbrand/res/bannerx/dnsrv-ent.png new file mode 100644 index 0000000..e649917 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-ent.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-ent_amd64.png b/shared/winbrand/res/bannerx/dnsrv-ent_amd64.png new file mode 100644 index 0000000..8dc831d Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-ent_amd64.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-sbs.png b/shared/winbrand/res/bannerx/dnsrv-sbs.png new file mode 100644 index 0000000..0ac3aee Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-sbs.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-std.png b/shared/winbrand/res/bannerx/dnsrv-std.png new file mode 100644 index 0000000..5ad048b Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-std.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-std_amd64.png b/shared/winbrand/res/bannerx/dnsrv-std_amd64.png new file mode 100644 index 0000000..410c1c7 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-std_amd64.png differ diff --git a/shared/winbrand/res/bannerx/dnsrv-web.png b/shared/winbrand/res/bannerx/dnsrv-web.png new file mode 100644 index 0000000..3b02ba4 Binary files /dev/null and b/shared/winbrand/res/bannerx/dnsrv-web.png differ diff --git a/shared/winbrand/res/bannerx/homesrv.png b/shared/winbrand/res/bannerx/homesrv.png new file mode 100644 index 0000000..b760097 Binary files /dev/null and b/shared/winbrand/res/bannerx/homesrv.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-embedded.png b/shared/winbrand/res/bannerx/xpclient-embedded.png new file mode 100644 index 0000000..d0d09b7 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-embedded.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-flp.png b/shared/winbrand/res/bannerx/xpclient-flp.png new file mode 100644 index 0000000..6f9424c Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-flp.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-mce.png b/shared/winbrand/res/bannerx/xpclient-mce.png new file mode 100644 index 0000000..a91f49f Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-mce.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-per.png b/shared/winbrand/res/bannerx/xpclient-per.png new file mode 100644 index 0000000..2e076fd Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-per.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-posready.png b/shared/winbrand/res/bannerx/xpclient-posready.png new file mode 100644 index 0000000..6e2f717 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-posready.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-pro.png b/shared/winbrand/res/bannerx/xpclient-pro.png new file mode 100644 index 0000000..a5463c1 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-pro.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-pro_amd64.png b/shared/winbrand/res/bannerx/xpclient-pro_amd64.png new file mode 100644 index 0000000..b43eb93 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-pro_amd64.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-pro_ia64.png b/shared/winbrand/res/bannerx/xpclient-pro_ia64.png new file mode 100644 index 0000000..ac5da47 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-pro_ia64.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-pro_ia64v2.png b/shared/winbrand/res/bannerx/xpclient-pro_ia64v2.png new file mode 100644 index 0000000..85f8331 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-pro_ia64v2.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-starter.png b/shared/winbrand/res/bannerx/xpclient-starter.png new file mode 100644 index 0000000..88492a0 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-starter.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-tabletpc.png b/shared/winbrand/res/bannerx/xpclient-tabletpc.png new file mode 100644 index 0000000..6df8a98 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-tabletpc.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-wepos.png b/shared/winbrand/res/bannerx/xpclient-wepos.png new file mode 100644 index 0000000..cf5222e Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-wepos.png differ diff --git a/shared/winbrand/res/bannerx/xpclient-wes.png b/shared/winbrand/res/bannerx/xpclient-wes.png new file mode 100644 index 0000000..e6b0b35 Binary files /dev/null and b/shared/winbrand/res/bannerx/xpclient-wes.png differ diff --git a/shared/winbrand/res/strip-anim.png b/shared/winbrand/res/strip-anim.png new file mode 100644 index 0000000..d52fda7 Binary files /dev/null and b/shared/winbrand/res/strip-anim.png differ diff --git a/shared/winbrand/res/strip-xp.png b/shared/winbrand/res/strip-static.png similarity index 100% rename from shared/winbrand/res/strip-xp.png rename to shared/winbrand/res/strip-static.png diff --git a/shared/winbrand/src/brand.c.in b/shared/winbrand/src/brand.c.in index 71d7e98..b8e69d5 100644 --- a/shared/winbrand/src/brand.c.in +++ b/shared/winbrand/src/brand.c.in @@ -6,22 +6,35 @@ // // PUBLIC FUNCTIONS // -GdkPixbuf* wintc_brand_get_banner( - GError** error +GdkPixbuf* wintc_brand_get_brand_pixmap( + WinTCBrandPart part, + GError** error ) { - return gdk_pixbuf_new_from_file( - "@CMAKE_INSTALL_PREFIX@/@WINTC_ASSETS_INSTALL_DIR@/brand/banner.png", - error - ); -} + gchar* path = NULL; -GdkPixbuf* wintc_brand_get_progress_strip( - GError** error -) -{ - return gdk_pixbuf_new_from_file( - "@CMAKE_INSTALL_PREFIX@/@WINTC_ASSETS_INSTALL_DIR@/brand/strip.png", - error - ); -} + switch (part) + { + case WINTC_BRAND_PART_BANNER: + path = "@CMAKE_INSTALL_PREFIX@/@WINTC_BRAND_DIR@/banner.png"; + break; + + case WINTC_BRAND_PART_BANNER_TALL: + path = "@CMAKE_INSTALL_PREFIX@/@WINTC_BRAND_DIR@/bannerx.png"; + break; + + case WINTC_BRAND_PART_STRIP_STATIC: + path = "@CMAKE_INSTALL_PREFIX@/@WINTC_BRAND_DIR@/strip-static.png"; + break; + + case WINTC_BRAND_PART_STRIP_ANIM: + path = "@CMAKE_INSTALL_PREFIX@/@WINTC_BRAND_DIR@/strip-anim.png"; + break; + + default: + g_critical("Unknown brand part %d", part); + return NULL; + } + + return gdk_pixbuf_new_from_file(path, error); +} \ No newline at end of file diff --git a/shared/winbrand/src/brand.h b/shared/winbrand/src/brand.h index a065e52..9aad855 100644 --- a/shared/winbrand/src/brand.h +++ b/shared/winbrand/src/brand.h @@ -4,8 +4,17 @@ #include #include -GdkPixbuf* wintc_brand_get_banner( - GError** error +typedef enum +{ + WINTC_BRAND_PART_BANNER = 0, + WINTC_BRAND_PART_BANNER_TALL, + WINTC_BRAND_PART_STRIP_STATIC = 10, + WINTC_BRAND_PART_STRIP_ANIM +} WinTCBrandPart; + +GdkPixbuf* wintc_brand_get_brand_pixmap( + WinTCBrandPart part, + GError** error ); #endif diff --git a/shell/run/src/dialog.c b/shell/run/src/dialog.c index 7660a42..7547804 100644 --- a/shell/run/src/dialog.c +++ b/shell/run/src/dialog.c @@ -220,7 +220,7 @@ static void wintc_run_dialog_init( wintc_widget_add_style_class( box_buttons, - WINTC_COMCTL_BUTTON_BOX_CLASS + WINTC_COMCTL_BUTTON_BOX_CSS_CLASS ); // Set OK button as default widget @@ -304,7 +304,7 @@ static void wintc_run_dialog_init_combobox( { if (error != NULL) { - WINTC_LOG_DEBUG("I can't populate run history: %s", error->message); + WINTC_LOG_USER_DEBUG("Can't get run history: %s", error->message); g_clear_error(&error); } @@ -328,7 +328,7 @@ static void wintc_run_dialog_init_combobox( ) ) { - WINTC_LOG_DEBUG("No recent runs."); + WINTC_LOG_USER_DEBUG("No recent runs."); return; } @@ -486,7 +486,7 @@ static void on_ok_button_clicked( { if (!wintc_append_run_history(cmdline, &error)) { - WINTC_LOG_DEBUG("I couldn't update run history: %s", error->message); + WINTC_LOG_USER_DEBUG("Can't update history: %s", error->message); g_clear_error(&error); } @@ -499,7 +499,7 @@ static void on_ok_button_clicked( // gchar* message = g_slice_alloc0(WINTC_GCHAR_BUFFER_SIZE); - WINTC_LOG_DEBUG("Run command failed: %s", error->message); + WINTC_LOG_USER_DEBUG("Run command failed: %s", error->message); switch (error->code) { diff --git a/shell/run/src/history.c b/shell/run/src/history.c index e1ef779..b662897 100644 --- a/shell/run/src/history.c +++ b/shell/run/src/history.c @@ -38,7 +38,7 @@ GList* wintc_get_run_history( if (!read_success) { - WINTC_LOG_DEBUG("No run history: %s", error->message); + WINTC_LOG_USER_DEBUG("No run history: %s", error->message); g_propagate_error(out_error, error); @@ -66,7 +66,7 @@ gboolean wintc_append_run_history( WINTC_SAFE_REF_CLEAR(out_error); - WINTC_LOG_DEBUG("Writing to run history"); + WINTC_LOG_USER_DEBUG("Writing to run history"); // Check if list is above maximum size? // @@ -108,7 +108,7 @@ gboolean wintc_append_run_history( if (!success) { - WINTC_LOG_DEBUG("Failed to write run history: %s", error->message); + WINTC_LOG_USER_DEBUG("Failed to write run history: %s", error->message); g_propagate_error(out_error, error); diff --git a/shell/winver/src/winver.c b/shell/winver/src/winver.c index eb42b70..a65d813 100644 --- a/shell/winver/src/winver.c +++ b/shell/winver/src/winver.c @@ -87,8 +87,14 @@ int main( GError* strip_error = NULL; GdkPixbuf* strip_pixbuf = NULL; - banner_pixbuf = wintc_brand_get_banner(&banner_error); - strip_pixbuf = wintc_brand_get_progress_strip(&strip_error); + banner_pixbuf = wintc_brand_get_brand_pixmap( + WINTC_BRAND_PART_BANNER, + &banner_error + ); + strip_pixbuf = wintc_brand_get_brand_pixmap( + WINTC_BRAND_PART_STRIP_STATIC, + &strip_error + ); // FIXME: Handle missing branding properly... bleh! // @@ -196,7 +202,7 @@ int main( wintc_widget_add_style_class( box_buttons, - WINTC_COMCTL_BUTTON_BOX_CLASS + WINTC_COMCTL_BUTTON_BOX_CSS_CLASS ); // Clear mem diff --git a/tools/bldutils/depmap/archpkg-maps b/tools/bldutils/depmap/archpkg-maps index 834c41e..2318acc 100644 --- a/tools/bldutils/depmap/archpkg-maps +++ b/tools/bldutils/depmap/archpkg-maps @@ -3,6 +3,7 @@ garcon-gtk3-->bt,rt-->garcon gdk-pixbuf2-->bt,rt-->gdk-pixbuf2 glib2-->bt,rt-->glib2 gtk3-->bt,rt-->gtk3 +lightdm-->bt,rt-->lightdm msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth python3-venv-->bt,rt-->python3 @@ -10,6 +11,7 @@ sass-->bt,rt-->ruby-sass wintc-comctl-->bt,rt-->wintc-comctl wintc-comgtk-->bt,rt-->wintc-comgtk wintc-exec-->bt,rt-->wintc-exec +wintc-msgina-->bt,rt-->wintc-msgina wintc-shelldpa-->bt,rt-->wintc-shelldpa wintc-shllang-->bt,rt-->wintc-shllang wintc-winbrand-->bt,rt-->wintc-winbrand diff --git a/tools/bldutils/depmap/deb-maps b/tools/bldutils/depmap/deb-maps index d27fb2a..cb99149 100644 --- a/tools/bldutils/depmap/deb-maps +++ b/tools/bldutils/depmap/deb-maps @@ -8,6 +8,8 @@ glib2-->bt-->libglib2.0-dev glib2-->rt-->libglib2.0-0 gtk3-->bt-->libgtk-3-dev gtk3-->rt-->libgtk-3-0 +lightdm-->bt-->liblightdm-gobject-dev +lightdm-->rt-->lightdm msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth python3-venv-->bt,rt-->python3-venv @@ -15,6 +17,7 @@ sass-->bt,rt-->ruby-sass wintc-comctl-->bt,rt-->libwintc-comctl wintc-comgtk-->bt,rt-->libwintc-comgtk wintc-exec-->bt,rt-->libwintc-exec +wintc-msgina-->bt,rt-->libwintc-msgina wintc-shelldpa-->bt,rt-->libwintc-shelldpa wintc-shllang-->bt,rt-->libwintc-shllang wintc-winbrand-->bt,rt-->libwintc-winbrand