11#include < gtk/gtk.h>
22#include < gio/gio.h>
3- #include < gio/gdesktopappinfo.h>
43#include < gtk-layer-shell/gtk-layer-shell.h>
54#include < stdlib.h>
65#include < string.h>
76
8- #define APP_DIR " /usr/share/applications/"
97#define WALLPAPER_DIR " /usr/share/wallpapers/"
108#define DEFAULT_WALLPAPER WALLPAPER_DIR " default_wallpaper.jpg" // Assume a default wallpaper file
119
12- typedef struct {
10+ struct DesktopData {
1311 GtkWidget *window;
1412 GtkWidget *overlay;
1513 GtkWidget *background_image;
16- GtkWidget *icon_view ;
14+ GtkWidget *flow_box ;
1715 GListModel *app_list;
18- } DesktopData ;
16+ };
1917
20- static void launch_app (GtkWidget *widget, gpointer user_data) {
21- GAppInfo *app_info = G_APP_INFO (user_data);
18+ static void launch_app (GAppInfo *app_info) {
2219 GError *error = NULL ;
2320 if (!g_app_info_launch (app_info, NULL , NULL , &error)) {
2421 g_printerr (" Failed to launch app: %s\n " , error->message );
2522 g_error_free (error);
2623 }
2724}
2825
29- static void populate_icons (GtkIconView *icon_view, GListModel *model) {
30- gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (model));
31- gtk_icon_view_set_pixbuf_column (icon_view, 0 );
32- gtk_icon_view_set_text_column (icon_view, 1 );
33- gtk_icon_view_set_activate_on_single_click (icon_view, TRUE );
34- g_signal_connect (icon_view, " item-activated" , G_CALLBACK (launch_app), NULL );
26+ static void on_icon_pressed (GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
27+ GtkEventController *controller = GTK_EVENT_CONTROLLER (gesture);
28+ GtkWidget *widget = gtk_event_controller_get_widget (controller);
29+ GAppInfo *app_info = (GAppInfo*)g_object_get_data (G_OBJECT (widget), " app_info" );
30+ if (app_info) {
31+ launch_app (app_info);
32+ }
33+ }
34+
35+ static void populate_flowbox (GtkFlowBox *flow_box, GListModel *model) {
36+ for (guint i = 0 ; i < g_list_model_get_n_items (model); i++) {
37+ GAppInfo *app_info = G_APP_INFO (g_list_model_get_object (model, i));
38+ if (!app_info) continue ;
39+ GIcon *gicon = g_app_info_get_icon (app_info);
40+ GtkWidget *image = gtk_image_new_from_gicon (gicon);
41+ gtk_image_set_pixel_size (GTK_IMAGE (image), 48 );
42+ GtkWidget *label = gtk_label_new (g_app_info_get_display_name (app_info));
43+ gtk_label_set_wrap (GTK_LABEL (label), TRUE );
44+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
45+ GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6 );
46+ gtk_box_append (GTK_BOX (box), image);
47+ gtk_box_append (GTK_BOX (box), label);
48+ gtk_widget_set_margin_start (box, 6 );
49+ gtk_widget_set_margin_end (box, 6 );
50+ gtk_widget_set_margin_top (box, 6 );
51+ gtk_widget_set_margin_bottom (box, 6 );
52+ gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
53+ gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
54+ g_object_set_data_full (G_OBJECT (box), " app_info" , g_object_ref (app_info), g_object_unref);
55+ GtkGesture *gesture = gtk_gesture_click_new ();
56+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 1 );
57+ g_signal_connect (gesture, " pressed" , G_CALLBACK (on_icon_pressed), NULL );
58+ gtk_widget_add_controller (box, GTK_EVENT_CONTROLLER (gesture));
59+ gtk_flow_box_append (flow_box, box);
60+ g_object_unref (app_info);
61+ }
3562}
3663
3764static GListModel *load_apps () {
3865 GListStore *store = g_list_store_new (G_TYPE_APP_INFO);
39- GDir *dir = g_dir_open (APP_DIR, 0 , NULL );
40- if (dir) {
41- const gchar *filename;
42- while ((filename = g_dir_read_name (dir)) != NULL ) {
43- if (g_str_has_suffix (filename, " .desktop" )) {
44- gchar *path = g_build_filename (APP_DIR, filename, NULL );
45- GDesktopAppInfo *app_info = g_desktop_app_info_new_from_filename (path);
46- if (app_info && g_app_info_should_show (G_APP_INFO (app_info))) {
47- g_list_store_append (store, app_info);
48- g_object_unref (app_info);
49- }
50- g_free (path);
51- }
66+ GList *apps = g_app_info_get_all ();
67+ for (GList *l = apps; l != NULL ; l = l->next ) {
68+ GAppInfo *app_info = G_APP_INFO (l->data );
69+ if (g_app_info_should_show (app_info)) {
70+ g_list_store_append (store, app_info);
5271 }
53- g_dir_close (dir);
5472 }
73+ g_list_free_full (apps, g_object_unref);
5574 return G_LIST_MODEL (store);
5675}
5776
58- static gboolean on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
59- if (event->button == 3 ) { // Right click
60- GtkWidget *menu = gtk_menu_new ();
61- GtkWidget *item_logout = gtk_menu_item_new_with_label (" Logout" );
62- GtkWidget *item_shutdown = gtk_menu_item_new_with_label (" Shutdown" );
63- GtkWidget *item_restart = gtk_menu_item_new_with_label (" Restart" );
64-
65- // Connect signals (placeholders, implement actual commands)
66- g_signal_connect (item_logout, " activate" , G_CALLBACK (system), (gpointer)" dm-tool switch-to-greeter" ); // Example
67- g_signal_connect (item_shutdown, " activate" , G_CALLBACK (system), (gpointer)" systemctl poweroff" );
68- g_signal_connect (item_restart, " activate" , G_CALLBACK (system), (gpointer)" systemctl reboot" );
69-
70- gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_logout);
71- gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_shutdown);
72- gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_restart);
73-
74- gtk_widget_show_all (menu);
75- gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent*)event);
76- return TRUE ;
77- }
78- return FALSE ;
77+ static void on_logout (GSimpleAction *action, GVariant *param, gpointer user_data) {
78+ system (" dm-tool switch-to-greeter" );
79+ }
80+
81+ static void on_shutdown (GSimpleAction *action, GVariant *param, gpointer user_data) {
82+ system (" systemctl poweroff" );
83+ }
84+
85+ static void on_restart (GSimpleAction *action, GVariant *param, gpointer user_data) {
86+ system (" systemctl reboot" );
87+ }
88+
89+ static void show_context_menu (double x, double y, DesktopData *data) {
90+ GMenu *menu = g_menu_new ();
91+ g_menu_append (menu, " Logout" , " app.logout" );
92+ g_menu_append (menu, " Shutdown" , " app.shutdown" );
93+ g_menu_append (menu, " Restart" , " app.restart" );
94+ GtkWidget *popover = gtk_popover_menu_new_from_model (G_MENU_MODEL (menu));
95+ gtk_widget_set_parent (popover, data->window );
96+ GdkRectangle point = { (int )x, (int )y, 0 , 0 };
97+ gtk_popover_set_pointing_to (GTK_POPOVER (popover), &point);
98+ gtk_popover_popup (GTK_POPOVER (popover));
99+ g_object_unref (menu);
100+ }
101+
102+ static void on_desktop_click_pressed (GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
103+ if (n_press != 1 ) return ;
104+ show_context_menu (x, y, (DesktopData*)user_data);
79105}
80106
81107static void activate (GtkApplication *app, gpointer user_data) {
82108 DesktopData *data = (DesktopData *)user_data;
83-
84109 data->window = gtk_application_window_new (app);
85110 gtk_window_set_title (GTK_WINDOW (data->window ), " Blue Desktop" );
86111 gtk_window_fullscreen (GTK_WINDOW (data->window ));
@@ -95,60 +120,73 @@ static void activate(GtkApplication *app, gpointer user_data) {
95120 gtk_layer_set_anchor (GTK_WINDOW (data->window ), GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE );
96121 gtk_layer_set_keyboard_mode (GTK_WINDOW (data->window ), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
97122
123+ // Add actions to app
124+ GSimpleAction *logout_action = g_simple_action_new (" logout" , NULL );
125+ g_signal_connect (logout_action, " activate" , G_CALLBACK (on_logout), NULL );
126+ g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (logout_action));
127+ g_object_unref (logout_action);
128+
129+ GSimpleAction *shutdown_action = g_simple_action_new (" shutdown" , NULL );
130+ g_signal_connect (shutdown_action, " activate" , G_CALLBACK (on_shutdown), NULL );
131+ g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (shutdown_action));
132+ g_object_unref (shutdown_action);
133+
134+ GSimpleAction *restart_action = g_simple_action_new (" restart" , NULL );
135+ g_signal_connect (restart_action, " activate" , G_CALLBACK (on_restart), NULL );
136+ g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (restart_action));
137+ g_object_unref (restart_action);
138+
98139 // Overlay for background and icons
99140 data->overlay = gtk_overlay_new ();
100141
101142 // Background image
102- data->background_image = gtk_image_new_from_file (DEFAULT_WALLPAPER);
143+ data->background_image = gtk_picture_new_for_filename (DEFAULT_WALLPAPER);
144+ gtk_picture_set_content_fit (GTK_PICTURE (data->background_image ), GTK_CONTENT_FIT_COVER);
103145 gtk_widget_set_hexpand (data->background_image , TRUE );
104146 gtk_widget_set_vexpand (data->background_image , TRUE );
105- gtk_image_set_pixel_size (GTK_IMAGE (data->background_image ), -1 ); // Scale to fit
106- gtk_overlay_add_overlay (GTK_OVERLAY (data->overlay ), data->background_image );
107-
108- // Icon view for desktop icons (apps)
109- data->icon_view = gtk_icon_view_new ();
110- gtk_widget_set_hexpand (data->icon_view , TRUE );
111- gtk_widget_set_vexpand (data->icon_view , TRUE );
112- gtk_icon_view_set_item_orientation (GTK_ICON_VIEW (data->icon_view ), GTK_ORIENTATION_VERTICAL);
113- gtk_icon_view_set_columns (GTK_ICON_VIEW (data->icon_view ), -1 );
114- gtk_icon_view_set_spacing (GTK_ICON_VIEW (data->icon_view ), 12 );
115- gtk_icon_view_set_item_padding (GTK_ICON_VIEW (data->icon_view ), 6 );
147+ gtk_overlay_set_child (GTK_OVERLAY (data->overlay ), data->background_image );
148+
149+ // Flow box for desktop icons (apps)
150+ data->flow_box = gtk_flow_box_new ();
151+ gtk_flow_box_set_column_spacing (GTK_FLOW_BOX (data->flow_box ), 12 );
152+ gtk_flow_box_set_row_spacing (GTK_FLOW_BOX (data->flow_box ), 12 );
153+ gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (data->flow_box ), GTK_SELECTION_NONE);
154+ gtk_widget_set_hexpand (data->flow_box , TRUE );
155+ gtk_widget_set_vexpand (data->flow_box , TRUE );
116156
117157 // Load and populate apps
118158 data->app_list = load_apps ();
119- populate_icons ( GTK_ICON_VIEW (data->icon_view ), data->app_list );
159+ populate_flowbox ( GTK_FLOW_BOX (data->flow_box ), data->app_list );
120160
121161 // Scrolled window for icons if many
122- GtkWidget *scrolled = gtk_scrolled_window_new (NULL , NULL );
123- gtk_container_add ( GTK_CONTAINER (scrolled), data->icon_view );
162+ GtkWidget *scrolled = gtk_scrolled_window_new ();
163+ gtk_scrolled_window_set_child ( GTK_SCROLLED_WINDOW (scrolled), data->flow_box );
124164 gtk_overlay_add_overlay (GTK_OVERLAY (data->overlay ), scrolled);
125165
126- gtk_container_add ( GTK_CONTAINER (data->window ), data->overlay );
166+ gtk_window_set_child ( GTK_WINDOW (data->window ), data->overlay );
127167
128168 // Handle right click
129- gtk_widget_add_events (data->window , GDK_BUTTON_PRESS_MASK);
130- g_signal_connect (data->window , " button-press-event" , G_CALLBACK (on_button_press), NULL );
169+ GtkGesture *gesture = gtk_gesture_click_new ();
170+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 3 );
171+ g_signal_connect (gesture, " pressed" , G_CALLBACK (on_desktop_click_pressed), data);
172+ gtk_widget_add_controller (data->overlay , GTK_EVENT_CONTROLLER (gesture));
131173
132174 // Set dark theme
133175 GtkSettings *settings = gtk_settings_get_default ();
134176 g_object_set (settings, " gtk-application-prefer-dark-theme" , TRUE , NULL );
135177 g_object_set (settings, " gtk-theme-name" , " Adwaita" , NULL ); // Or custom theme
136178
137- gtk_widget_show_all (data->window );
179+ gtk_widget_set_visible (data->window , TRUE );
138180}
139181
140182int main (int argc, char **argv) {
141- GtkApplication *app = gtk_application_new (" org.blueenvironment.desktop" , G_APPLICATION_FLAGS_NONE );
183+ GtkApplication *app = gtk_application_new (" org.blueenvironment.desktop" , G_APPLICATION_DEFAULT_FLAGS );
142184 DesktopData data = {0 };
143-
144185 g_signal_connect (app, " activate" , G_CALLBACK (activate), &data);
145-
146186 int status = g_application_run (G_APPLICATION (app), argc, argv);
147187 g_object_unref (app);
148-
149188 if (data.app_list ) {
150189 g_object_unref (data.app_list );
151190 }
152-
153191 return status;
154192}
0 commit comments