diff options
Diffstat (limited to 'applets/wncklet')
-rw-r--r-- | applets/wncklet/Makefile.am | 18 | ||||
-rw-r--r-- | applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in | 2 | ||||
-rw-r--r-- | applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in | 10 | ||||
-rw-r--r-- | applets/wncklet/showdesktop.c | 111 | ||||
-rw-r--r-- | applets/wncklet/showdesktop.h | 1 | ||||
-rw-r--r-- | applets/wncklet/wayland-backend.c | 752 | ||||
-rw-r--r-- | applets/wncklet/wayland-backend.h | 46 | ||||
-rwxr-xr-x | applets/wncklet/wayland-protocol/generate-code.sh | 20 | ||||
-rw-r--r-- | applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h | 594 | ||||
-rw-r--r-- | applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c | 104 | ||||
-rw-r--r-- | applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1.xml | 259 | ||||
-rw-r--r-- | applets/wncklet/window-list.c | 592 | ||||
-rw-r--r-- | applets/wncklet/window-list.ui | 602 | ||||
-rw-r--r-- | applets/wncklet/window-menu.c | 67 | ||||
-rw-r--r-- | applets/wncklet/wncklet.c | 25 | ||||
-rw-r--r-- | applets/wncklet/wncklet.h | 4 | ||||
-rw-r--r-- | applets/wncklet/workspace-switcher.c | 529 |
17 files changed, 3133 insertions, 603 deletions
diff --git a/applets/wncklet/Makefile.am b/applets/wncklet/Makefile.am index b933f81b..14f8a5c7 100644 --- a/applets/wncklet/Makefile.am +++ b/applets/wncklet/Makefile.am @@ -1,3 +1,5 @@ +AUTOMAKE_OPTIONS = subdir-objects + AM_CPPFLAGS = \ $(LIBMATE_PANEL_APPLET_CFLAGS) \ $(WNCKLET_CFLAGS) \ @@ -28,6 +30,20 @@ WNCKLET_LDADD = \ $(WNCKLET_LIBS) \ $(LIBMATE_PANEL_APPLET_LIBS) +if ENABLE_WAYLAND +WNCKLET_SOURCES += \ + wayland-backend.c \ + wayland-backend.h \ + wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c \ + wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h + +WNCKLET_LDADD += \ + $(WAYLAND_LIBS) + +AM_CPPFLAGS += \ + $(WAYLAND_CFLAGS) +endif + if WNCKLET_INPROCESS APPLET_IN_PROCESS = true APPLET_LOCATION = $(pkglibdir)/libwnck-applet.so @@ -62,9 +78,9 @@ $(applet_in_files): $(applet_in_files).in Makefile $(applet_DATA): $(applet_in_files) $(AM_V_GEN) $(MSGFMT) --desktop --keyword= --keyword=Name --keyword=Description --template $< -d $(top_srcdir)/po -o $@ +service_in_files = org.mate.panel.applet.WnckletFactory.service.in if !WNCKLET_INPROCESS servicedir = $(datadir)/dbus-1/services -service_in_files = org.mate.panel.applet.WnckletFactory.service.in service_DATA = $(service_in_files:.service.in=.service) org.mate.panel.applet.WnckletFactory.service: $(service_in_files) diff --git a/applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in b/applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in index 490edcc1..99e7815c 100644 --- a/applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in +++ b/applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in @@ -37,7 +37,7 @@ Description=Switch between open windows using buttons # Translators: Do NOT translate or transliterate this text (this is an icon file name)! Icon=mate-panel-window-list MateComponentId=OAFIID:MATE_TasklistApplet;OAFIID:MATE_WindowListApplet; -Platforms=X11; +Platforms=X11;Wayland; X-MATE-Bugzilla-Bugzilla=MATE X-MATE-Bugzilla-Product=mate-panel X-MATE-Bugzilla-Component=window list diff --git a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in index 0f2d871d..93f32b46 100644 --- a/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in +++ b/applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in @@ -20,5 +20,15 @@ <summary>Move windows to current workspace when unminimized</summary> <description>If true, then when unminimizing a window, move it to the current workspace. Otherwise, switch to the workspace of the window.</description> </key> + <key name="scroll-enabled" type="b"> + <default>true</default> + <summary>Whether to enable mouse scrolling in the switch window list</summary> + <description>If true, enable mouse scrolling in window list, otherwise disable mouse scrolling in window list.</description> + </key> + <key name="middle-click-close" type="b"> + <default>true</default> + <summary>Close window on middle mouse click</summary> + <description>If true, then clicking the middle mouse button over a taskbar item will close the window.</description> + </key> </schema> </schemalist> diff --git a/applets/wncklet/showdesktop.c b/applets/wncklet/showdesktop.c index 7999174a..190077f1 100644 --- a/applets/wncklet/showdesktop.c +++ b/applets/wncklet/showdesktop.c @@ -22,20 +22,18 @@ */ #ifdef HAVE_CONFIG_H - #include <config.h> -#endif - -#ifndef HAVE_X11 -#error file should only be built when HAVE_X11 is enabled +#include <config.h> #endif #include <glib/gi18n.h> #include <gtk/gtk.h> -#include <gdk/gdkx.h> +#ifdef HAVE_X11 +#include <gdk/gdkx.h> #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include <libwnck/libwnck.h> +#endif #include "wncklet.h" #include "showdesktop.h" @@ -45,7 +43,6 @@ #define TIMEOUT_ACTIVATE_SECONDS 1 #define SHOW_DESKTOP_ICON "user-desktop" - typedef struct { /* widgets */ GtkWidget* applet; @@ -318,7 +315,7 @@ static gboolean do_not_eat_button_press(GtkWidget* widget, GdkEventButton* event { if (event->button != 1) { - g_signal_stop_emission_by_name(widget, "button_press_event"); + g_signal_stop_emission_by_name(widget, "button-press-event"); } return FALSE; @@ -361,21 +358,32 @@ static void show_desktop_applet_realized(MatePanelApplet* applet, gpointer data) sdd = (ShowDesktopData*) data; - if (sdd->wnck_screen != NULL) - g_signal_handlers_disconnect_by_func(sdd->wnck_screen, show_desktop_changed_callback, sdd); - if (sdd->icon_theme != NULL) g_signal_handlers_disconnect_by_func(sdd->icon_theme, theme_changed_callback, sdd); screen = gtk_widget_get_screen(sdd->applet); - sdd->wnck_screen = wnck_screen_get(gdk_x11_screen_get_screen_number (screen)); if (sdd->wnck_screen != NULL) - wncklet_connect_while_alive(sdd->wnck_screen, "showing_desktop_changed", G_CALLBACK(show_desktop_changed_callback), sdd, sdd->applet); - else - g_warning("Could not get WnckScreen!"); + g_signal_handlers_disconnect_by_func(sdd->wnck_screen, show_desktop_changed_callback, sdd); - show_desktop_changed_callback(sdd->wnck_screen, sdd); + sdd->wnck_screen = NULL; + +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + sdd->wnck_screen = wnck_screen_get (gdk_x11_screen_get_screen_number (screen)); + if (sdd->wnck_screen != NULL) + wncklet_connect_while_alive (sdd->wnck_screen, + "showing_desktop_changed", + G_CALLBACK (show_desktop_changed_callback), + sdd, + sdd->applet); + else + g_warning ("Could not get WnckScreen!"); + } +#endif /* HAVE_X11 */ + + show_desktop_changed_callback (sdd->wnck_screen, sdd); sdd->icon_theme = gtk_icon_theme_get_for_screen (screen); wncklet_connect_while_alive(sdd->icon_theme, "changed", G_CALLBACK(theme_changed_callback), sdd, sdd->applet); @@ -418,7 +426,9 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet) sdd->size = mate_panel_applet_get_size(MATE_PANEL_APPLET(sdd->applet)); - g_signal_connect(G_OBJECT(sdd->applet), "realize", G_CALLBACK(show_desktop_applet_realized), sdd); + g_signal_connect (sdd->applet, "realize", + G_CALLBACK (show_desktop_applet_realized), + sdd); sdd->button = gtk_toggle_button_new (); @@ -438,23 +448,29 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet) atk_obj = gtk_widget_get_accessible(sdd->button); atk_object_set_name (atk_obj, _("Show Desktop Button")); - g_signal_connect(G_OBJECT(sdd->button), "button_press_event", G_CALLBACK(do_not_eat_button_press), NULL); + g_signal_connect (sdd->button, "button-press-event", + G_CALLBACK(do_not_eat_button_press), + NULL); - g_signal_connect(G_OBJECT(sdd->button), "toggled", G_CALLBACK(button_toggled_callback), sdd); + g_signal_connect (sdd->button, "toggled", + G_CALLBACK (button_toggled_callback), + sdd); gtk_container_set_border_width(GTK_CONTAINER(sdd->button), 0); gtk_container_add(GTK_CONTAINER(sdd->button), sdd->image); gtk_container_add(GTK_CONTAINER(sdd->applet), sdd->button); - g_signal_connect (G_OBJECT(sdd->button), "size_allocate", G_CALLBACK(button_size_allocated), sdd); + g_signal_connect (sdd->button, "size-allocate", + G_CALLBACK (button_size_allocated), + sdd); /* FIXME: Update this comment. */ /* we have to bind change_orient before we do applet_widget_add since we need to get an initial change_orient signal to set our initial oriantation, and we get that during the _add call */ - g_signal_connect(G_OBJECT (sdd->applet), "change_orient", G_CALLBACK (applet_change_orient), sdd); - - mate_panel_applet_set_background_widget(MATE_PANEL_APPLET (sdd->applet), GTK_WIDGET(sdd->applet)); + g_signal_connect (sdd->applet, "change-orient", + G_CALLBACK (applet_change_orient), + sdd); action_group = gtk_action_group_new("ShowDesktop Applet Actions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); @@ -464,12 +480,18 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet) action_group); g_object_unref(action_group); - g_signal_connect(G_OBJECT(sdd->applet), "destroy", G_CALLBACK(applet_destroyed), sdd); + g_signal_connect (sdd->applet, "destroy", + G_CALLBACK (applet_destroyed), + sdd); gtk_drag_dest_set(GTK_WIDGET(sdd->button), 0, NULL, 0, 0); - g_signal_connect(G_OBJECT(sdd->button), "drag_motion", G_CALLBACK (button_drag_motion), sdd); - g_signal_connect(G_OBJECT(sdd->button), "drag_leave", G_CALLBACK (button_drag_leave), sdd); + g_signal_connect (sdd->button, "drag-motion", + G_CALLBACK (button_drag_motion), + sdd); + g_signal_connect (sdd->button, "drag-leave", + G_CALLBACK (button_drag_leave), + sdd); gtk_widget_show_all(sdd->applet); @@ -502,19 +524,34 @@ static void display_about_dialog(GtkAction* action, ShowDesktopData* sdd) "comments", _("This button lets you hide all windows and show the desktop."), "copyright", _("Copyright \xc2\xa9 2002 Red Hat, Inc.\n" "Copyright \xc2\xa9 2011 Perberos\n" - "Copyright \xc2\xa9 2012-2020 MATE developers"), + "Copyright \xc2\xa9 2012-2021 MATE developers"), "documenters", documenters, "icon-name", SHOW_DESKTOP_ICON, "logo-icon-name", SHOW_DESKTOP_ICON, "translator-credits", _("translator-credits"), "version", VERSION, - "website", "http://www.mate-desktop.org/", + "website", PACKAGE_URL, NULL); } static void button_toggled_callback(GtkWidget* button, ShowDesktopData* sdd) { - if (!gdk_x11_screen_supports_net_wm_hint(gtk_widget_get_screen(button), gdk_atom_intern("_NET_SHOWING_DESKTOP", FALSE))) + gboolean can_show_desktop; + +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + can_show_desktop = gdk_x11_screen_supports_net_wm_hint(gtk_widget_get_screen(button), + gdk_atom_intern("_NET_SHOWING_DESKTOP", + FALSE)); + } + else +#endif + { /* not using X11 */ + can_show_desktop = FALSE; + } + + if (!can_show_desktop) { static GtkWidget* dialog = NULL; @@ -527,11 +564,17 @@ static void button_toggled_callback(GtkWidget* button, ShowDesktopData* sdd) return; } - dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Your window manager does not support the show desktop button, or you are not running a window manager.")); + dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Your window manager does not support the show desktop button, or you are not running a window manager.")); g_object_add_weak_pointer(G_OBJECT(dialog), (gpointer) &dialog); - g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL); + g_signal_connect (dialog, "response", + G_CALLBACK(gtk_widget_destroy), + NULL); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_window_set_screen(GTK_WINDOW(dialog), gtk_widget_get_screen(button)); @@ -540,16 +583,20 @@ static void button_toggled_callback(GtkWidget* button, ShowDesktopData* sdd) return; } +#ifdef HAVE_X11 if (sdd->wnck_screen != NULL) wnck_screen_toggle_showing_desktop(sdd->wnck_screen, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))); +#endif /* HAVE_X11 */ update_button_display (sdd); } static void show_desktop_changed_callback(WnckScreen* screen, ShowDesktopData* sdd) { +#ifdef HAVE_X11 if (sdd->wnck_screen != NULL) - sdd->showing_desktop = wnck_screen_get_showing_desktop(sdd->wnck_screen); + sdd->showing_desktop = (wnck_screen_get_showing_desktop(sdd->wnck_screen) != FALSE); +#endif /* HAVE_X11 */ update_button_state (sdd); } diff --git a/applets/wncklet/showdesktop.h b/applets/wncklet/showdesktop.h index e82ed713..7aa7e60d 100644 --- a/applets/wncklet/showdesktop.h +++ b/applets/wncklet/showdesktop.h @@ -39,4 +39,3 @@ gboolean show_desktop_applet_fill(MatePanelApplet* applet); #endif - diff --git a/applets/wncklet/wayland-backend.c b/applets/wncklet/wayland-backend.c new file mode 100644 index 00000000..f10b2d65 --- /dev/null +++ b/applets/wncklet/wayland-backend.c @@ -0,0 +1,752 @@ +/* Wncklet applet Wayland backend */ + +/* + * Copyright (C) 2019 William Wold + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <config.h> + +#ifndef HAVE_WAYLAND +#error file should only be compiled when HAVE_WAYLAND is enabled +#endif + +#include <gdk/gdkwayland.h> +#include <gio/gdesktopappinfo.h> + +#include "wayland-backend.h" +#include "wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h" + +/*shorter than wnck-tasklist due to common use of larger fonts*/ +#define TASKLIST_TEXT_MAX_WIDTH 16 + +/*In the future this could be changable from the panel-prefs dialog*/ +static const int max_button_width = 180; +static const int icon_size = 16; + +typedef struct +{ + GtkWidget *menu; + GtkWidget *maximize; + GtkWidget *minimize; + GtkWidget *on_top; + GtkWidget *close; +} ContextMenu; + +typedef struct +{ + GtkWidget *list; + GtkWidget *outer_box; + ContextMenu *context_menu; + struct zwlr_foreign_toplevel_manager_v1 *manager; +} TasklistManager; + +typedef struct +{ + GtkWidget *button; + GtkWidget *icon; + GtkWidget *label; + struct zwlr_foreign_toplevel_handle_v1 *toplevel; + gboolean active; + gboolean maximized; + gboolean minimized; + gboolean fullscreen; +} ToplevelTask; + +static const char *tasklist_manager_key = "tasklist_manager"; +static const char *toplevel_task_key = "toplevel_task"; + +static gboolean has_initialized = FALSE; +static struct wl_registry *wl_registry_global = NULL; +static uint32_t foreign_toplevel_manager_global_id = 0; +static uint32_t foreign_toplevel_manager_global_version = 0; + +static ToplevelTask *toplevel_task_new (TasklistManager *tasklist, struct zwlr_foreign_toplevel_handle_v1 *handle); + +guint buttons, tasklist_width; + +static void +wl_registry_handle_global (void *_data, + struct wl_registry *registry, + uint32_t id, + const char *interface, + uint32_t version) +{ + /* pull out needed globals */ + if (strcmp (interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) + { + g_warn_if_fail (zwlr_foreign_toplevel_manager_v1_interface.version == 2); + foreign_toplevel_manager_global_id = id; + foreign_toplevel_manager_global_version = + MIN((uint32_t)zwlr_foreign_toplevel_manager_v1_interface.version, version); + } +} + +static void +wl_registry_handle_global_remove (void *_data, + struct wl_registry *_registry, + uint32_t id) +{ + if (id == foreign_toplevel_manager_global_id) + { + foreign_toplevel_manager_global_id = 0; + } +} + +static const struct wl_registry_listener wl_registry_listener = { + .global = wl_registry_handle_global, + .global_remove = wl_registry_handle_global_remove, +}; + +static void +wayland_tasklist_init_if_needed (void) +{ + if (has_initialized) + return; + + GdkDisplay *gdk_display = gdk_display_get_default (); + g_return_if_fail (gdk_display); + g_return_if_fail (GDK_IS_WAYLAND_DISPLAY (gdk_display)); + + struct wl_display *wl_display = gdk_wayland_display_get_wl_display (gdk_display); + wl_registry_global = wl_display_get_registry (wl_display); + wl_registry_add_listener (wl_registry_global, &wl_registry_listener, NULL); + wl_display_roundtrip (wl_display); + + if (!foreign_toplevel_manager_global_id) + g_warning ("%s not supported by Wayland compositor", + zwlr_foreign_toplevel_manager_v1_interface.name); + + has_initialized = TRUE; +} + +static void +foreign_toplevel_manager_handle_toplevel (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + TasklistManager *tasklist = data; + ToplevelTask *task = toplevel_task_new (tasklist, toplevel); + gtk_box_pack_start (GTK_BOX (tasklist->list), task->button, TRUE, TRUE, 0); +} + +static void +foreign_toplevel_manager_handle_finished (void *data, + struct zwlr_foreign_toplevel_manager_v1 *manager) +{ + TasklistManager *tasklist = data; + + tasklist->manager = NULL; + zwlr_foreign_toplevel_manager_v1_destroy (manager); + + if (tasklist->outer_box) + g_object_set_data (G_OBJECT (tasklist->outer_box), + tasklist_manager_key, + NULL); + + g_free (tasklist); +} + +static const struct zwlr_foreign_toplevel_manager_v1_listener foreign_toplevel_manager_listener = { + .toplevel = foreign_toplevel_manager_handle_toplevel, + .finished = foreign_toplevel_manager_handle_finished, +}; + +static void +tasklist_manager_disconnected_from_widget (TasklistManager *tasklist) +{ + if (tasklist->list) + { + GList *children = gtk_container_get_children (GTK_CONTAINER (tasklist->list)); + for (GList *iter = children; iter != NULL; iter = g_list_next (iter)) + gtk_widget_destroy (GTK_WIDGET (iter->data)); + g_list_free(children); + tasklist->list = NULL; + } + + if (tasklist->outer_box) + tasklist->outer_box = NULL; + + if (tasklist->manager) + zwlr_foreign_toplevel_manager_v1_stop (tasklist->manager); + + if (tasklist->context_menu) + { + gtk_widget_destroy (tasklist->context_menu->menu); + g_free (tasklist->context_menu); + tasklist->context_menu = NULL; + } +} + +static void +menu_on_maximize (GtkMenuItem *item, gpointer user_data) +{ + ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key); + if (task->toplevel) { + if (task->maximized) { + zwlr_foreign_toplevel_handle_v1_unset_maximized (task->toplevel); + } else { + zwlr_foreign_toplevel_handle_v1_set_maximized (task->toplevel); + } + } +} + +static void +menu_on_minimize (GtkMenuItem *item, gpointer user_data) +{ + ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key); + if (task->toplevel) { + if (task->minimized) { + zwlr_foreign_toplevel_handle_v1_unset_minimized (task->toplevel); + } else { + zwlr_foreign_toplevel_handle_v1_set_minimized (task->toplevel); + } + } +} + +static void +menu_on_close (GtkMenuItem *item, gpointer user_data) +{ + ToplevelTask *task = g_object_get_data (G_OBJECT (item), toplevel_task_key); + if (task->toplevel) { + zwlr_foreign_toplevel_handle_v1_close (task->toplevel); + } +} + +static ContextMenu * +context_menu_new () +{ + ContextMenu *menu = g_new0 (ContextMenu, 1); + menu->menu = gtk_menu_new (); + menu->maximize = gtk_menu_item_new (); + menu->minimize = gtk_menu_item_new (); + menu->on_top = gtk_check_menu_item_new_with_label ("Always On Top"); + menu->close = gtk_menu_item_new_with_label ("Close"); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->maximize); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->minimize); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), gtk_separator_menu_item_new ()); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->on_top); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), gtk_separator_menu_item_new ()); + gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), menu->close); + + gtk_widget_show_all (menu->menu); + + g_signal_connect (menu->maximize, "activate", G_CALLBACK (menu_on_maximize), NULL); + g_signal_connect (menu->minimize, "activate", G_CALLBACK (menu_on_minimize), NULL); + g_signal_connect (menu->close, "activate", G_CALLBACK (menu_on_close), NULL); + gtk_widget_set_sensitive (menu->on_top, FALSE); + return menu; +} + +static TasklistManager * +tasklist_manager_new (void) +{ + if (!foreign_toplevel_manager_global_id) + return NULL; + + TasklistManager *tasklist = g_new0 (TasklistManager, 1); + tasklist->list = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous (GTK_BOX (tasklist->list), TRUE); + tasklist->outer_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (tasklist->outer_box), tasklist->list, FALSE, FALSE, 0); + gtk_widget_show (tasklist->list); + tasklist->manager = wl_registry_bind (wl_registry_global, + foreign_toplevel_manager_global_id, + &zwlr_foreign_toplevel_manager_v1_interface, + foreign_toplevel_manager_global_version); + zwlr_foreign_toplevel_manager_v1_add_listener (tasklist->manager, + &foreign_toplevel_manager_listener, + tasklist); + g_object_set_data_full (G_OBJECT (tasklist->outer_box), + tasklist_manager_key, + tasklist, + (GDestroyNotify)tasklist_manager_disconnected_from_widget); + tasklist->context_menu = context_menu_new (); + return tasklist; +} + +static void +foreign_toplevel_handle_title (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + const char *title) +{ + ToplevelTask *task = data; + + if (task->label) + { + gtk_label_set_label (GTK_LABEL (task->label), title); + } +} + +static void +foreign_toplevel_handle_app_id (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + const char *app_id) +{ + ToplevelTask *task = data; + + gchar *app_id_lower = g_utf8_strdown (app_id, -1); + gchar *desktop_app_id = g_strdup_printf ("%s.desktop", app_id_lower); + GDesktopAppInfo *app_info = g_desktop_app_info_new (desktop_app_id); + + if (app_info) { + GIcon *icon = g_app_info_get_icon (G_APP_INFO (app_info)); + if (icon) { + gtk_image_set_from_gicon (GTK_IMAGE (task->icon), icon, GTK_ICON_SIZE_MENU); + goto cleanup; + } + } + gtk_image_set_from_icon_name (GTK_IMAGE (task->icon), app_id_lower, GTK_ICON_SIZE_MENU); + +cleanup: + if (app_info) { + g_object_unref (G_OBJECT (app_info)); + } + g_free (app_id_lower); + g_free (desktop_app_id); +} + +static void +foreign_toplevel_handle_output_enter (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output) +{ + /* ignore */ +} + +static void +foreign_toplevel_handle_output_leave (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_output *output) +{ + /* ignore */ +} + +static void +foreign_toplevel_handle_state (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_array *state) +{ + ToplevelTask *task = data; + + task->active = FALSE; + task->maximized = FALSE; + task->minimized = FALSE; + task->fullscreen = FALSE; + + enum zwlr_foreign_toplevel_handle_v1_state *i; + wl_array_for_each (i, state) + { + switch (*i) + { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: + task->active = TRUE; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: + task->maximized = TRUE; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: + task->minimized = TRUE; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: + task->fullscreen = TRUE; + break; + default: + break; + } + } + + gtk_button_set_relief (GTK_BUTTON (task->button), task->active ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE); +} + +static void +foreign_toplevel_handle_done (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + /* ignore */ +} + +static void +foreign_toplevel_handle_closed (void *data, + struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + ToplevelTask *task = data; + + if (task->button) + { + GtkOrientation orient; + GtkWidget *button; + GtkWidget *box; + GtkWidget *outer_box = gtk_widget_get_parent (GTK_WIDGET (task->button)); + gtk_widget_destroy (task->button); + buttons = buttons -1; + + if (buttons == 0) + return; + + /* We don't need to modify button size on a vertical panel*/ + orient = gtk_orientable_get_orientation (GTK_ORIENTABLE (outer_box)); + if (orient == GTK_ORIENTATION_VERTICAL) + return; + + /* Horizontal panel: if buttons can now fit + * with both labels and icons show them + */ + if (tasklist_width / buttons > icon_size * 3) + { + GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box)); + while (children != NULL) + { + button = GTK_WIDGET (children->data); + + /* If maximum width buttons fix, expand to that dimension*/ + if (buttons * max_button_width < tasklist_width) + gtk_widget_set_size_request (button, max_button_width, -1); + + /* Otherwise expand remaining buttons to fill the tasklist*/ + else + gtk_widget_set_size_request (button, tasklist_width / buttons, -1); + + gtk_widget_show_all (button); + children = children->next; + } + } + /* If buttons with icons will fit, bring them back*/ + else if (tasklist_width / buttons > icon_size * 2) + { + GtkWidget *widget; + GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box)); + while (children != NULL) + { + button = GTK_WIDGET (children->data); + box = gtk_bin_get_child (GTK_BIN (button)); + GList* contents = gtk_container_get_children (GTK_CONTAINER (box)); + while (contents != NULL) + { + widget = GTK_WIDGET (contents->data); + if (GTK_IS_LABEL (widget)) + gtk_widget_hide (widget); + + if (GTK_IS_IMAGE (widget)) + gtk_widget_show (widget); + + contents = contents->next; + gtk_widget_show (box); + gtk_widget_show (button); + } + + children = children->next; + gtk_widget_set_size_request (button, tasklist_width / buttons, -1); + } + } + /* If we still cannot fit labels or icons, just fill the available space*/ + else + { + GList* children = gtk_container_get_children (GTK_CONTAINER (outer_box)); + while (children != NULL) + { + button = GTK_WIDGET (children->data); + gtk_widget_set_size_request (button, tasklist_width / buttons, -1); + children = children->next; + } + } + } +} + +static const struct zwlr_foreign_toplevel_handle_v1_listener foreign_toplevel_handle_listener = { + .title = foreign_toplevel_handle_title, + .app_id = foreign_toplevel_handle_app_id, + .output_enter = foreign_toplevel_handle_output_enter, + .output_leave = foreign_toplevel_handle_output_leave, + .state = foreign_toplevel_handle_state, + .done = foreign_toplevel_handle_done, + .closed = foreign_toplevel_handle_closed, +}; + +static void +toplevel_task_disconnected_from_widget (ToplevelTask *task) +{ + struct zwlr_foreign_toplevel_handle_v1 *toplevel = task->toplevel; + + task->button = NULL; + task->icon = NULL; + task->label = NULL; + task->toplevel = NULL; + + if (toplevel) + zwlr_foreign_toplevel_handle_v1_destroy (toplevel); + + g_free (task); +} + +static void +toplevel_task_handle_clicked (GtkButton *button, ToplevelTask *task) +{ + if (task->toplevel) + { + if (task->active) + { + zwlr_foreign_toplevel_handle_v1_set_minimized (task->toplevel); + } + else + { + GdkDisplay *gdk_display = gtk_widget_get_display (GTK_WIDGET (button)); + GdkSeat *gdk_seat = gdk_display_get_default_seat (gdk_display); + struct wl_seat *wl_seat = gdk_wayland_seat_get_wl_seat (gdk_seat); + zwlr_foreign_toplevel_handle_v1_activate (task->toplevel, wl_seat); + } + } +} + +static gboolean on_toplevel_button_press (GtkWidget *button, GdkEvent *event, TasklistManager *tasklist) +{ + /* Assume event is a button press */ + if (((GdkEventButton*)event)->button == GDK_BUTTON_SECONDARY) + { + ContextMenu *menu = tasklist->context_menu; + ToplevelTask *task = g_object_get_data (G_OBJECT (button), toplevel_task_key); + + g_object_set_data (G_OBJECT (menu->maximize), toplevel_task_key, task); + g_object_set_data (G_OBJECT (menu->minimize), toplevel_task_key, task); + g_object_set_data (G_OBJECT (menu->close), toplevel_task_key, task); + + gtk_menu_item_set_label (GTK_MENU_ITEM (menu->minimize), + task->minimized ? "Unminimize" : "Minimize"); + gtk_menu_item_set_label (GTK_MENU_ITEM (menu->maximize), + task->maximized ? "Unmaximize" : "Maximize"); + + gtk_menu_popup_at_widget (GTK_MENU (menu->menu), button, + GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_SOUTH_WEST, event); + return TRUE; + } + else + { + return FALSE; + } +} + +static ToplevelTask * +toplevel_task_new (TasklistManager *tasklist, struct zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + ToplevelTask *task = g_new0 (ToplevelTask, 1); + GtkWidget *button; + GtkOrientation orient; + + buttons = buttons + 1; + orient = gtk_orientable_get_orientation (GTK_ORIENTABLE (tasklist->outer_box)); + task->button = gtk_button_new (); + g_signal_connect (task->button, "clicked", G_CALLBACK (toplevel_task_handle_clicked), task); + + task->icon = gtk_image_new_from_icon_name ("unknown", icon_size); + + task->label = gtk_label_new (""); + gtk_label_set_max_width_chars (GTK_LABEL (task->label), TASKLIST_TEXT_MAX_WIDTH); + gtk_label_set_ellipsize (GTK_LABEL (task->label), PANGO_ELLIPSIZE_END); + gtk_label_set_xalign (GTK_LABEL (task->label), 0.0); + + GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (box), task->icon, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (box), task->label, TRUE, TRUE, 2); + + gtk_container_add (GTK_CONTAINER (task->button), box); + gtk_widget_set_name (task->button , "tasklist-button"); + gtk_widget_show_all (task->button); + + /* Buttons on a vertical panel are not affected by how many are needed + * GTK handles compressing contents as needed as the window width tells + * GTK how much space to allocate the label and icon. Buttons will use + * the full width of a vertical panel without any special attention + * so break out here instead of breaking the vertical panel case + */ + + if (orient == GTK_ORIENTATION_VERTICAL) + { + gtk_widget_show_all (task->button); + task->toplevel = toplevel; + zwlr_foreign_toplevel_handle_v1_add_listener (toplevel, + &foreign_toplevel_handle_listener, + task); + g_object_set_data_full (G_OBJECT (task->button), + toplevel_task_key, + task, + (GDestroyNotify)toplevel_task_disconnected_from_widget); + + g_signal_connect (task->button, "button-press-event", + G_CALLBACK (on_toplevel_button_press), + tasklist); + + return task; + } + + /* On horizontal panels, GTK does not by default limit the width of the tasklist + * as it does not run out of space in the window until the entire panel is used, + * leaving buttons at full width until then and overflowing all other applets + * + * Thus we must get the tasklist's allocated width when extra space remains, + * which will be most of the distance between the handle and the next applet + * From there, we can expand buttons and/or hide elements as needed + */ + + + tasklist_width = gtk_widget_get_allocated_width (GTK_WIDGET (tasklist->outer_box)); + + /* First button can be buggy with this so hardcode it to expand to the limit */ + if (buttons == 1) + gtk_widget_set_size_request (task->button, max_button_width, -1); + + /* if the number of buttons forces width to less than 3x the icon size, shrink them */ + if ((buttons != 0) && (tasklist_width > 1 )&& (tasklist_width / buttons < (icon_size * 3))) + { + /* adjust the current button first or it can be missed */ + if (tasklist_width / buttons > icon_size * 2) + { + gtk_widget_hide (task->label); + gtk_widget_show (task->icon); + } + else + { + gtk_widget_show (task->label); + gtk_widget_hide (task->icon); + } + gtk_widget_show (box); + gtk_widget_show (task->button); + + /* iterate over all the buttons, first hide labels + * then hide icons and bring back labels + */ + GtkWidget *widget; + + GList* children = gtk_container_get_children (GTK_CONTAINER (tasklist->list)); + while (children != NULL) + { + button = GTK_WIDGET (children->data); + box = gtk_bin_get_child (GTK_BIN (button)); + + /* hide labels of all buttons but show icons if only icons will fit */ + if (tasklist_width / buttons > icon_size * 2) + { + /* find the icon and the label, show just the icon */ + GList* contents = gtk_container_get_children (GTK_CONTAINER (box)); + + while (contents != NULL) + { + widget = GTK_WIDGET (contents->data); + if (GTK_IS_LABEL (widget)) + gtk_widget_hide (widget); + + if (GTK_IS_IMAGE (widget)) + gtk_widget_show (widget); + + contents = contents->next; + } + } + else + { + /* find the icon and the label, show just the label as it is more + * compressable than the icon. Though less meaningful at this size, + * it is enough to keep the tasklist from disappearing on themes + * that do not set borders around tasklist buttons. + * This is same behavior as on x11 save that an extreme number of + * buttons (50+ on 700px of space) can still overflow + */ + + GList* contents = gtk_container_get_children (GTK_CONTAINER (box)); + while (contents != NULL) + { + widget = GTK_WIDGET (contents->data); + if (GTK_IS_LABEL (widget)) + gtk_widget_show (widget); + + if (GTK_IS_IMAGE (widget)) + gtk_widget_hide (widget); + + contents = contents->next; + } + } + /*expand buttons with labels or everything hidden to fit remaining space*/ + gtk_widget_set_size_request (button, tasklist_width / buttons, -1); + /*show the button and any contents that fit, then get the next button*/ + gtk_widget_show (box); + gtk_widget_show (button); + + children = children->next; + } + } + else + { + GList* children = gtk_container_get_children (GTK_CONTAINER(tasklist->list)); + while (children != NULL) + { + button = GTK_WIDGET (children->data); + if (((buttons ) * max_button_width < tasklist_width) || (buttons == 1)) + + /*Don't let buttons expand over the maximum button size*/ + gtk_widget_set_size_request (button, max_button_width, -1); + + else + /*if full width buttons won't fit, size them to just fill the tasklist*/ + gtk_widget_set_size_request (button, tasklist_width / buttons, -1); + + children = children->next; + } + gtk_widget_show_all (task->button); + } + + /*Reset the tasklist width after button adjustments*/ + tasklist_width = gtk_widget_get_allocated_width (GTK_WIDGET (tasklist->outer_box)); + + task->toplevel = toplevel; + zwlr_foreign_toplevel_handle_v1_add_listener (toplevel, + &foreign_toplevel_handle_listener, + task); + g_object_set_data_full (G_OBJECT (task->button), + toplevel_task_key, + task, + (GDestroyNotify)toplevel_task_disconnected_from_widget); + + g_signal_connect (task->button, "button-press-event", + G_CALLBACK (on_toplevel_button_press), + tasklist); + + return task; +} + +GtkWidget* +wayland_tasklist_new () +{ + wayland_tasklist_init_if_needed (); + TasklistManager *tasklist = tasklist_manager_new (); + if (!tasklist) + return gtk_label_new ("Shell does not support WLR Foreign Toplevel Control"); + return tasklist->outer_box; +} + +static TasklistManager * +tasklist_widget_get_tasklist (GtkWidget* tasklist_widget) +{ + return g_object_get_data (G_OBJECT (tasklist_widget), tasklist_manager_key); +} + +void +wayland_tasklist_set_orientation (GtkWidget* tasklist_widget, GtkOrientation orient) +{ + TasklistManager *tasklist = tasklist_widget_get_tasklist (tasklist_widget); + g_return_if_fail(tasklist); + gtk_orientable_set_orientation (GTK_ORIENTABLE (tasklist->list), orient); + gtk_orientable_set_orientation (GTK_ORIENTABLE (tasklist->outer_box), orient); +} diff --git a/applets/wncklet/wayland-backend.h b/applets/wncklet/wayland-backend.h new file mode 100644 index 00000000..e2402236 --- /dev/null +++ b/applets/wncklet/wayland-backend.h @@ -0,0 +1,46 @@ +/* Wncklet applet Wayland backend */ + +/* + * Copyright (C) 2019 William Wold + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _WNCKLET_APPLET_WAYLAND_BACKEND_H_ +#define _WNCKLET_APPLET_WAYLAND_BACKEND_H_ + +#ifdef PACKAGE_NAME /* only check HAVE_WAYLAND if config.h has been included */ +#ifndef HAVE_WAYLAND +#error file should only be included when HAVE_WAYLAND is enabled +#endif +#endif + +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#ifdef __cplusplus +extern "C" { +#endif + +GtkWidget* wayland_tasklist_new (void); +void wayland_tasklist_set_orientation (GtkWidget* tasklist_widget, GtkOrientation orient); + +#ifdef __cplusplus +} +#endif + +#endif /* _WNCKLET_APPLET_WAYLAND_BACKEND_H_ */ + diff --git a/applets/wncklet/wayland-protocol/generate-code.sh b/applets/wncklet/wayland-protocol/generate-code.sh new file mode 100755 index 00000000..90b248b2 --- /dev/null +++ b/applets/wncklet/wayland-protocol/generate-code.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# This script uses wayland-scanner to generate C bindings from Wayland protocol XML definitions +# It only needs to be called with the XML files change, and is not called as part of the build system + +# Fail the script if anything goes wrong +set -euo pipefail + +# Get the directory this script is in +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +echo "Looking for Wayland protocols in $SCRIPT_DIR" + +# Loop through all XML files in the same directory as this script +for PROTO_FILE_PATH in $(ls "$SCRIPT_DIR"/*.xml); do + # Strip the path and the .xml extension + PROTO_NAME=$(basename "$PROTO_FILE_PATH" .xml) + echo "Generating C bindings for $PROTO_NAME" + wayland-scanner -c client-header "$PROTO_FILE_PATH" "$SCRIPT_DIR/$PROTO_NAME-client.h" + wayland-scanner -c private-code "$PROTO_FILE_PATH" "$SCRIPT_DIR/$PROTO_NAME-code.c" +done diff --git a/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h new file mode 100644 index 00000000..c5f28aab --- /dev/null +++ b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h @@ -0,0 +1,594 @@ +/* Generated by wayland-scanner 1.18.0 */ + +#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client-core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol + * @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps + * - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel + * @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright + * <pre> + * + * Copyright © 2018 Ilia Bozhinov + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + * </pre> + */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct zwlr_foreign_toplevel_handle_v1; +struct zwlr_foreign_toplevel_manager_v1; + +/** + * @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1 + * @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description + * + * The purpose of this protocol is to enable the creation of taskbars + * and docks by providing them with a list of opened applications and + * letting them request certain actions on them, like maximizing, etc. + * + * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + * toplevel window will be sent via the toplevel event + * @section page_iface_zwlr_foreign_toplevel_manager_v1_api API + * See @ref iface_zwlr_foreign_toplevel_manager_v1. + */ +/** + * @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface + * + * The purpose of this protocol is to enable the creation of taskbars + * and docks by providing them with a list of opened applications and + * letting them request certain actions on them, like maximizing, etc. + * + * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + * toplevel window will be sent via the toplevel event + */ +extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface; +/** + * @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1 + * @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description + * + * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + * window. Each app may have multiple opened toplevels. + * + * Each toplevel has a list of outputs it is visible on, conveyed to the + * client with the output_enter and output_leave events. + * @section page_iface_zwlr_foreign_toplevel_handle_v1_api API + * See @ref iface_zwlr_foreign_toplevel_handle_v1. + */ +/** + * @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface + * + * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + * window. Each app may have multiple opened toplevels. + * + * Each toplevel has a list of outputs it is visible on, conveyed to the + * client with the output_enter and output_leave events. + */ +extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface; + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + * @struct zwlr_foreign_toplevel_manager_v1_listener + */ +struct zwlr_foreign_toplevel_manager_v1_listener { + /** + * a toplevel has been created + * + * This event is emitted whenever a new toplevel window is + * created. It is emitted for all toplevels, regardless of the app + * that has created them. + * + * All initial details of the toplevel(title, app_id, states, etc.) + * will be sent immediately after this event via the corresponding + * events in zwlr_foreign_toplevel_handle_v1. + */ + void (*toplevel)(void *data, + struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, + struct zwlr_foreign_toplevel_handle_v1 *toplevel); + /** + * the compositor has finished with the toplevel manager + * + * This event indicates that the compositor is done sending + * events to the zwlr_foreign_toplevel_manager_v1. The server will + * destroy the object immediately after sending this request, so it + * will become invalid and the client should free any resources + * associated with it. + */ + void (*finished)(void *data, + struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1); +}; + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +static inline int +zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, + const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0 + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void +zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data); +} + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void * +zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +static inline uint32_t +zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void +zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + * + * Indicates the client no longer wishes to receive events for new toplevels. + * However the compositor may emit further toplevel_created events, until + * the finished event is emitted. + * + * The client must not send any more requests after this one. + */ +static inline void +zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, + ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP); +} + +#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * types of states on the toplevel + * + * The different states that a toplevel can have. These have the same meaning + * as the states with the same names defined in xdg-toplevel + */ +enum zwlr_foreign_toplevel_handle_v1_state { + /** + * the toplevel is maximized + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0, + /** + * the toplevel is minimized + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1, + /** + * the toplevel is active + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2, + /** + * the toplevel is fullscreen + * @since 2 + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3, +}; +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2 +#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */ + +#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM +enum zwlr_foreign_toplevel_handle_v1_error { + /** + * the provided rectangle is invalid + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0, +}; +#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */ + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * @struct zwlr_foreign_toplevel_handle_v1_listener + */ +struct zwlr_foreign_toplevel_handle_v1_listener { + /** + * title change + * + * This event is emitted whenever the title of the toplevel + * changes. + */ + void (*title)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const char *title); + /** + * app-id change + * + * This event is emitted whenever the app-id of the toplevel + * changes. + */ + void (*app_id)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const char *app_id); + /** + * toplevel entered an output + * + * This event is emitted whenever the toplevel becomes visible on + * the given output. A toplevel may be visible on multiple outputs. + */ + void (*output_enter)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_output *output); + /** + * toplevel left an output + * + * This event is emitted whenever the toplevel stops being + * visible on the given output. It is guaranteed that an + * entered-output event with the same output has been emitted + * before this event. + */ + void (*output_leave)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_output *output); + /** + * the toplevel state changed + * + * This event is emitted immediately after the + * zlw_foreign_toplevel_handle_v1 is created and each time the + * toplevel state changes, either because of a compositor action or + * because of a request in this protocol. + */ + void (*state)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_array *state); + /** + * all information about the toplevel has been sent + * + * This event is sent after all changes in the toplevel state + * have been sent. + * + * This allows changes to the zwlr_foreign_toplevel_handle_v1 + * properties to be seen as atomic, even if they happen via + * multiple events. + */ + void (*done)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1); + /** + * this toplevel has been destroyed + * + * This event means the toplevel has been destroyed. It is + * guaranteed there won't be any more events for this + * zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes + * inert so any requests will be ignored except the destroy + * request. + */ + void (*closed)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1); +}; + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +static inline int +zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9 + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2 + +/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data); +} + +/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */ +static inline void * +zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1); +} + +static inline uint32_t +zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be maximized. If the maximized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unmaximized. If the maximized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be minimized. If the minimized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unminimized. If the minimized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Request that this toplevel be activated on the given seat. + * There is no guarantee the toplevel will be actually activated. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, seat); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Send a request to the toplevel to close itself. The compositor would + * typically use a shell-specific method to carry out this request, for + * example by sending the xdg_toplevel.close event. However, this gives + * no guarantees the toplevel will actually be destroyed. If and when + * this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + * be emitted. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * The rectangle of the surface specified in this request corresponds to + * the place where the app using this protocol represents the given toplevel. + * It can be used by the compositor as a hint for some operations, e.g + * minimizing. The client is however not required to set this, in which + * case the compositor is free to decide some default value. + * + * If the client specifies more than one rectangle, only the last one is + * considered. + * + * The dimensions are given in surface-local coordinates. + * Setting width=height=0 removes the already-set rectangle. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, surface, x, y, width, height); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Destroys the zwlr_foreign_toplevel_handle_v1 object. + * + * This request should be called either when the client does not want to + * use the toplevel anymore or after the closed event to finalize the + * destruction of the object. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be fullscreened on the given output. If the + * fullscreen state and/or the outputs the toplevel is visible on actually + * change, this will be indicated by the state and output_enter/leave + * events. + * + * The output parameter is only a hint to the compositor. Also, if output + * is NULL, the compositor should decide which output the toplevel will be + * fullscreened on, if at all. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, output); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unfullscreened. If the fullscreen state + * actually changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c new file mode 100644 index 00000000..ef335644 --- /dev/null +++ b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c @@ -0,0 +1,104 @@ +/* Generated by wayland-scanner 1.18.0 */ + +/* + * Copyright © 2018 Ilia Bozhinov + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface; + +static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = { + NULL, + &zwlr_foreign_toplevel_handle_v1_interface, + &wl_seat_interface, + &wl_surface_interface, + NULL, + NULL, + NULL, + NULL, + &wl_output_interface, + &wl_output_interface, + &wl_output_interface, +}; + +static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = { + { "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = { + { "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 }, + { "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = { + "zwlr_foreign_toplevel_manager_v1", 2, + 1, zwlr_foreign_toplevel_manager_v1_requests, + 2, zwlr_foreign_toplevel_manager_v1_events, +}; + +static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = { + { "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 }, + { "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 }, + { "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 }, + { "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = { + { "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 }, + { "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 }, + { "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = { + "zwlr_foreign_toplevel_handle_v1", 2, + 10, zwlr_foreign_toplevel_handle_v1_requests, + 7, zwlr_foreign_toplevel_handle_v1_events, +}; + diff --git a/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 00000000..a97738f8 --- /dev/null +++ b/applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,259 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_foreign_toplevel_management_unstable_v1"> + <copyright> + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + </copyright> + + <interface name="zwlr_foreign_toplevel_manager_v1" version="2"> + <description summary="list and control opened apps"> + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + </description> + + <event name="toplevel"> + <description summary="a toplevel has been created"> + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + </description> + <arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/> + </event> + + <request name="stop"> + <description summary="stop sending events"> + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + </description> + </request> + + <event name="finished"> + <description summary="the compositor has finished with the toplevel manager"> + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + </description> + </event> + </interface> + + <interface name="zwlr_foreign_toplevel_handle_v1" version="2"> + <description summary="an opened toplevel"> + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + </description> + + <event name="title"> + <description summary="title change"> + This event is emitted whenever the title of the toplevel changes. + </description> + <arg name="title" type="string"/> + </event> + + <event name="app_id"> + <description summary="app-id change"> + This event is emitted whenever the app-id of the toplevel changes. + </description> + <arg name="app_id" type="string"/> + </event> + + <event name="output_enter"> + <description summary="toplevel entered an output"> + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + </description> + <arg name="output" type="object" interface="wl_output"/> + </event> + + <event name="output_leave"> + <description summary="toplevel left an output"> + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + </description> + <arg name="output" type="object" interface="wl_output"/> + </event> + + <request name="set_maximized"> + <description summary="requests that the toplevel be maximized"> + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + </description> + </request> + + <request name="unset_maximized"> + <description summary="requests that the toplevel be unmaximized"> + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + </description> + </request> + + <request name="set_minimized"> + <description summary="requests that the toplevel be minimized"> + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + </description> + </request> + + <request name="unset_minimized"> + <description summary="requests that the toplevel be unminimized"> + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + </description> + </request> + + <request name="activate"> + <description summary="activate the toplevel"> + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + </description> + <arg name="seat" type="object" interface="wl_seat"/> + </request> + + <enum name="state"> + <description summary="types of states on the toplevel"> + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + </description> + + <entry name="maximized" value="0" summary="the toplevel is maximized"/> + <entry name="minimized" value="1" summary="the toplevel is minimized"/> + <entry name="activated" value="2" summary="the toplevel is active"/> + <entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/> + </enum> + + <event name="state"> + <description summary="the toplevel state changed"> + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + </description> + + <arg name="state" type="array"/> + </event> + + <event name="done"> + <description summary="all information about the toplevel has been sent"> + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + </description> + </event> + + <request name="close"> + <description summary="request that the toplevel be closed"> + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + </description> + </request> + + <request name="set_rectangle"> + <description summary="the rectangle which represents the toplevel"> + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + </description> + + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="x" type="int"/> + <arg name="y" type="int"/> + <arg name="width" type="int"/> + <arg name="height" type="int"/> + </request> + + <enum name="error"> + <entry name="invalid_rectangle" value="0" + summary="the provided rectangle is invalid"/> + </enum> + + <event name="closed"> + <description summary="this toplevel has been destroyed"> + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + </description> + </event> + + <request name="destroy" type="destructor"> + <description summary="destroy the zwlr_foreign_toplevel_handle_v1 object"> + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + </description> + </request> + + <!-- Version 2 additions --> + + <request name="set_fullscreen" since="2"> + <description summary="request that the toplevel be fullscreened"> + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + </description> + <arg name="output" type="object" interface="wl_output" allow-null="true"/> + </request> + + <request name="unset_fullscreen" since="2"> + <description summary="request that the toplevel be unfullscreened"> + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + </description> + </request> + </interface> +</protocol> diff --git a/applets/wncklet/window-list.c b/applets/wncklet/window-list.c index 40c5f07b..93785cc1 100644 --- a/applets/wncklet/window-list.c +++ b/applets/wncklet/window-list.c @@ -8,9 +8,7 @@ * */ -#ifdef HAVE_CONFIG_H - #include <config.h> -#endif +#include <config.h> #include <string.h> @@ -19,12 +17,18 @@ #include <glib/gi18n.h> #include <gtk/gtk.h> -#define WNCK_I_KNOW_THIS_IS_UNSTABLE -#include <libwnck/libwnck.h> #include <gio/gio.h> -#ifdef HAVE_WINDOW_PREVIEWS + +#ifdef HAVE_X11 #include <gdk/gdkx.h> -#endif +#define WNCK_I_KNOW_THIS_IS_UNSTABLE +#include <libwnck/libwnck.h> +#endif /* HAVE_X11 */ + +#ifdef HAVE_WAYLAND +#include <gdk/gdkwayland.h> +#include "wayland-backend.h" +#endif /* HAVE_WAYLAND */ #define MATE_DESKTOP_USE_UNSTABLE_API #include <libmate-desktop/mate-desktop-utils.h> @@ -34,9 +38,16 @@ #define WINDOW_LIST_ICON "mate-panel-window-list" #define WINDOW_LIST_SCHEMA "org.mate.panel.applet.window-list" + #ifdef HAVE_WINDOW_PREVIEWS #define WINDOW_LIST_PREVIEW_SCHEMA "org.mate.panel.applet.window-list-previews" -#endif +#endif /* HAVE_WINDOW_PREVIEWS */ + +typedef enum { + TASKLIST_NEVER_GROUP, + TASKLIST_AUTO_GROUP, + TASKLIST_ALWAYS_GROUP +} TasklistGroupingType; typedef struct { GtkWidget* applet; @@ -48,8 +59,11 @@ typedef struct { gint thumbnail_size; #endif gboolean include_all_workspaces; - WnckTasklistGroupingType grouping; + + TasklistGroupingType grouping; gboolean move_unminimized_windows; + gboolean scroll_enable; + gboolean middle_click_close; GtkOrientation orientation; int size; @@ -57,13 +71,13 @@ typedef struct { gboolean needs_hints; #endif - GtkIconTheme* icon_theme; - /* Properties: */ GtkWidget* properties_dialog; + GtkWidget* wayland_info_label; GtkWidget* show_current_radio; GtkWidget* show_all_radio; #ifdef HAVE_WINDOW_PREVIEWS + GtkWidget* window_thumbnail_box; GtkWidget* show_thumbnails_check; GtkWidget* thumbnail_size_label; GtkWidget* thumbnail_size_spin; @@ -71,9 +85,13 @@ typedef struct { GtkWidget* never_group_radio; GtkWidget* auto_group_radio; GtkWidget* always_group_radio; - GtkWidget* minimized_windows_label; GtkWidget* move_minimized_radio; + GtkWidget* mouse_scroll_check; + GtkWidget* middle_click_close_check; GtkWidget* change_workspace_radio; + GtkWidget* minimized_windows_box; + GtkWidget* window_grouping_box; + GtkWidget* window_list_content_box; GSettings* settings; #ifdef HAVE_WINDOW_PREVIEWS @@ -98,9 +116,79 @@ static void tasklist_update(TasklistData* tasklist) gtk_widget_set_size_request(GTK_WIDGET(tasklist->tasklist), tasklist->size, -1); } - wnck_tasklist_set_grouping(WNCK_TASKLIST(tasklist->tasklist), tasklist->grouping); - wnck_tasklist_set_include_all_workspaces(WNCK_TASKLIST(tasklist->tasklist), tasklist->include_all_workspaces); - wnck_tasklist_set_switch_workspace_on_unminimize(WNCK_TASKLIST(tasklist->tasklist), tasklist->move_unminimized_windows); +#ifdef HAVE_X11 + if (WNCK_IS_TASKLIST(tasklist->tasklist)) + { + WnckTasklistGroupingType grouping; + switch (tasklist->grouping) + { + case TASKLIST_NEVER_GROUP: + grouping = WNCK_TASKLIST_NEVER_GROUP; + break; + case TASKLIST_AUTO_GROUP: + grouping = WNCK_TASKLIST_AUTO_GROUP; + break; + case TASKLIST_ALWAYS_GROUP: + grouping = WNCK_TASKLIST_ALWAYS_GROUP; + break; + default: + grouping = WNCK_TASKLIST_NEVER_GROUP; + } + wnck_tasklist_set_grouping(WNCK_TASKLIST(tasklist->tasklist), grouping); + wnck_tasklist_set_include_all_workspaces(WNCK_TASKLIST(tasklist->tasklist), tasklist->include_all_workspaces); + wnck_tasklist_set_switch_workspace_on_unminimize(WNCK_TASKLIST(tasklist->tasklist), tasklist->move_unminimized_windows); + wnck_tasklist_set_scroll_enabled (WNCK_TASKLIST(tasklist->tasklist), tasklist->scroll_enable); + wnck_tasklist_set_middle_click_close (WNCK_TASKLIST (tasklist->tasklist), tasklist->middle_click_close); + } +#endif /* HAVE_X11 */ + + /* Not implemented for Wayland */ +} + +static void tasklist_apply_orientation(TasklistData* tasklist) +{ +#ifdef HAVE_X11 + if (WNCK_IS_TASKLIST(tasklist->tasklist)) + { + wnck_tasklist_set_orientation(WNCK_TASKLIST(tasklist->tasklist), tasklist->orientation); + } +#endif /* HAVE_X11 */ + +#ifdef HAVE_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) + { + wayland_tasklist_set_orientation(tasklist->tasklist, tasklist->orientation); + } +#endif +} + +static void tasklist_set_button_relief(TasklistData* tasklist, GtkReliefStyle relief) +{ +#ifdef HAVE_X11 + if (WNCK_IS_TASKLIST(tasklist->tasklist)) + { + wnck_tasklist_set_button_relief(WNCK_TASKLIST(tasklist->tasklist), relief); + } +#endif /* HAVE_X11 */ + + /* Not implemented for Wayland */ +} + +static const int* tasklist_get_size_hint_list(TasklistData* tasklist, int* n_elements) +{ +#ifdef HAVE_X11 + if (WNCK_IS_TASKLIST(tasklist->tasklist)) + { + return wnck_tasklist_get_size_hint_list(WNCK_TASKLIST(tasklist->tasklist), n_elements); + } + else +#endif /* HAVE_X11 */ + + { + /* Not implemented for Wayland */ + *n_elements = 0; + return NULL; + } } static void response_cb(GtkWidget* widget, int id, TasklistData* tasklist) @@ -115,11 +203,6 @@ static void response_cb(GtkWidget* widget, int id, TasklistData* tasklist) } } -static void applet_realized(MatePanelApplet* applet, TasklistData* tasklist) -{ - tasklist->icon_theme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(tasklist->applet)); -} - static void applet_change_orient(MatePanelApplet* applet, MatePanelAppletOrient orient, TasklistData* tasklist) { GtkOrientation new_orient; @@ -141,7 +224,7 @@ static void applet_change_orient(MatePanelApplet* applet, MatePanelAppletOrient return; tasklist->orientation = new_orient; - wnck_tasklist_set_orientation (WNCK_TASKLIST (tasklist->tasklist), new_orient); + tasklist_apply_orientation (tasklist); tasklist_update(tasklist); } @@ -153,99 +236,141 @@ static void applet_change_background(MatePanelApplet* applet, MatePanelAppletBac case PANEL_NO_BACKGROUND: case PANEL_COLOR_BACKGROUND: case PANEL_PIXMAP_BACKGROUND: - wnck_tasklist_set_button_relief(WNCK_TASKLIST(tasklist->tasklist), GTK_RELIEF_NONE); + tasklist_set_button_relief(tasklist, GTK_RELIEF_NONE); break; } } +#ifdef HAVE_X11 #ifdef HAVE_WINDOW_PREVIEWS -static GdkPixbuf *preview_window_thumbnail (WnckWindow *wnck_window, TasklistData *tasklist) +static cairo_surface_t* +preview_window_thumbnail (WnckWindow *wnck_window, + TasklistData *tasklist, + int *thumbnail_width, + int *thumbnail_height, + int *thumbnail_scale) { GdkWindow *window; - GdkPixbuf *screenshot; - GdkPixbuf *thumbnail; - guchar *pixels; + Window win; + cairo_surface_t *thumbnail; + cairo_t *cr; double ratio; - int width, height; - int scale; + int width, height, scale; - window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), wnck_window_get_xid (wnck_window)); + win = wnck_window_get_xid (wnck_window); - if (window == NULL) + if ((window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), win)) == NULL) + { return NULL; + } - scale = gdk_window_get_scale_factor (window); + *thumbnail_scale = scale = gdk_window_get_scale_factor (window); width = gdk_window_get_width (window) * scale; height = gdk_window_get_height (window) * scale; - /* Generate window screenshot for preview */ - screenshot = gdk_pixbuf_get_from_window (window, 0, 0, width / scale, height / scale); - g_object_unref (window); - - if (screenshot == NULL) - return NULL; - - /* Determine whether the contents of the screenshot are empty */ - pixels = gdk_pixbuf_get_pixels (screenshot); - if (!g_strcmp0 ((const char *)pixels, "")) - { - g_object_unref (screenshot); - return NULL; - } - /* Scale to configured size while maintaining aspect ratio */ if (width > height) { - ratio = (double) height / (double) width; - width = MIN(width, tasklist->thumbnail_size); - height = width * ratio; + int max_size = MIN (width, tasklist->thumbnail_size * scale); + ratio = (double) max_size / (double) width; + *thumbnail_width = max_size; + *thumbnail_height = (int) ((double) height * ratio); } else { - ratio = (double) width / (double) height; - height = MIN(height, tasklist->thumbnail_size); - width = height * ratio; + int max_size = MIN (height, tasklist->thumbnail_size * scale); + ratio = (double) max_size / (double) height; + *thumbnail_height = max_size; + *thumbnail_width = (int) ((double) width * ratio); + } + + gdk_x11_display_error_trap_push (gdk_window_get_display (window)); + + thumbnail = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + *thumbnail_width, + *thumbnail_height); + cairo_surface_set_device_scale (thumbnail, scale, scale); + cr = cairo_create (thumbnail); + cairo_scale (cr, ratio, ratio); + gdk_cairo_set_source_window (cr, window, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); + + if (gdk_x11_display_error_trap_pop (gdk_window_get_display (window))) + { + cairo_surface_destroy (thumbnail); + thumbnail = NULL; } - thumbnail = gdk_pixbuf_scale_simple (screenshot, width, height, GDK_INTERP_BILINEAR); - g_object_unref (screenshot); + g_object_unref (window); return thumbnail; } +static int g_int_compare(gconstpointer a, gconstpointer b) +{ + gint a_val = GPOINTER_TO_INT(a); + gint b_val = GPOINTER_TO_INT(b); + if (a_val > b_val) return -1; + if (a_val == b_val) return 0; + return 1; +} + +static int find_offset(GList *list, gdouble target) +{ + GList *node = list; + while (node != NULL) { + int value = GPOINTER_TO_INT(node->data); + if (value <= target) + return value; + node = node->next; + } + return -1; +} + #define PREVIEW_PADDING 5 -static void preview_window_reposition (TasklistData *tasklist, GdkPixbuf *thumbnail) +static void +preview_window_reposition (WnckTasklist *tl, + TasklistData *tasklist, + int width, + int height, + int scale) { + /* Known issues: + * - When grouping is toggled the previews won't be centered correctly until a new window is opened or one is closed. + * - Previews are not shown at all for grouped windows, this function is not called when hovering over those. + */ + GdkMonitor *monitor; GdkRectangle monitor_geom; - int x_pos, y_pos; - int width, height; + MatePanelAppletOrient orient; + gdouble x_pos, y_pos; + int x_offset, y_offset; - width = gdk_pixbuf_get_width (thumbnail); - height = gdk_pixbuf_get_height (thumbnail); - - /* Resize window to fit thumbnail */ - gtk_window_resize (GTK_WINDOW (tasklist->preview), width, height); - - /* Set position at pointer, then re-adjust from there to just outside of the pointer */ - gtk_window_set_position (GTK_WINDOW (tasklist->preview), GTK_WIN_POS_MOUSE); - gtk_window_get_position (GTK_WINDOW (tasklist->preview), &x_pos, &y_pos); + /* Get mouse position */ + gdk_device_get_position_double (gdk_seat_get_pointer (gdk_display_get_default_seat (gdk_display_get_default ())), NULL, &x_pos, &y_pos); /* Get geometry of monitor where tasklist is located to calculate correct position of preview */ monitor = gdk_display_get_monitor_at_point (gdk_display_get_default (), x_pos, y_pos); gdk_monitor_get_geometry (monitor, &monitor_geom); + /* Get the position where the window list applet starts */ + gdk_window_get_origin (gtk_widget_get_window (gtk_widget_get_parent (GTK_WIDGET(tl))), &x_offset, &y_offset); + + /* Get panel orientation */ + orient = mate_panel_applet_get_orient (MATE_PANEL_APPLET (tasklist->applet)); + /* Add padding to clear the panel */ - switch (mate_panel_applet_get_orient (MATE_PANEL_APPLET (tasklist->applet))) + switch (orient) { case MATE_PANEL_APPLET_ORIENT_LEFT: - x_pos = monitor_geom.width + monitor_geom.x - (width + tasklist->size) - PREVIEW_PADDING; + x_pos = monitor_geom.width + monitor_geom.x - width - tasklist->size - PREVIEW_PADDING; break; case MATE_PANEL_APPLET_ORIENT_RIGHT: x_pos = tasklist->size + PREVIEW_PADDING; break; case MATE_PANEL_APPLET_ORIENT_UP: - y_pos = monitor_geom.height + monitor_geom.y - (height + tasklist->size) - PREVIEW_PADDING; + y_pos = monitor_geom.height + monitor_geom.y - height - tasklist->size - PREVIEW_PADDING; break; case MATE_PANEL_APPLET_ORIENT_DOWN: default: @@ -253,24 +378,77 @@ static void preview_window_reposition (TasklistData *tasklist, GdkPixbuf *thumbn break; } + /* Collect the allocation.x/y values of each button into lists. + * We need to iterate over all of them because grouped buttons will be the last children, + * even though they are positioned at the beginning. And not all buttons will have the exact same width. + * This allows us to avoid off-by-one errors that would cause the preview to be positioned over the adjacent button. */ + GList *alloc_x_list = NULL; + GList *alloc_y_list = NULL; + GtkAllocation last_alloc; + GList* children = gtk_container_get_children (GTK_CONTAINER(tl)); + while (children != NULL) + { + if (g_strcmp0 (gtk_widget_get_name (children->data), "tasklist-button") == 0) { + GtkAllocation alloc; + gtk_widget_get_allocation (children->data, &alloc); + + /* Skip grouped buttons: these usually have alloc width/heigh=1, except right after grouping is toggled. + * Then simply open or close a new window to get the correct offset. */ + if (alloc.width < 2 || alloc.height < 2) + { + children = children->next; + continue; + } + + /* Keep x and y offsets in sorted lists */ + alloc_x_list = g_list_insert_sorted (alloc_x_list, GINT_TO_POINTER(alloc.x), g_int_compare); + alloc_y_list = g_list_insert_sorted (alloc_y_list, GINT_TO_POINTER(alloc.y), g_int_compare); + + /* The width/height from the last allocation will be used for centering the preview. + * It might be off by a pixel because not all buttons have the exact same width/height but this isn't critical. */ + last_alloc = alloc; + } + children = children->next; + } + + /* Center preview at the midpoint of the tasklist button */ + if (orient == MATE_PANEL_APPLET_ORIENT_LEFT || orient == MATE_PANEL_APPLET_ORIENT_RIGHT) + { + /* Vertical panel */ + y_pos = y_offset + find_offset (alloc_y_list, y_pos - y_offset) + (last_alloc.height - height) / 2; + y_pos = y_pos < PREVIEW_PADDING ? PREVIEW_PADDING : y_pos; + } + else if (orient == MATE_PANEL_APPLET_ORIENT_UP || orient == MATE_PANEL_APPLET_ORIENT_DOWN) + { + /* Horizontal panel */ + x_pos = x_offset + find_offset (alloc_x_list, x_pos - x_offset) + (last_alloc.width - width) / 2; + x_pos = x_pos < PREVIEW_PADDING ? PREVIEW_PADDING : x_pos; + } + + g_list_free (alloc_x_list); + g_list_free (alloc_y_list); + gtk_window_move (GTK_WINDOW (tasklist->preview), x_pos, y_pos); } -static gboolean preview_window_draw (GtkWidget *widget, cairo_t *cr, GdkPixbuf *thumbnail) +static gboolean preview_window_draw (GtkWidget *widget, cairo_t *cr, cairo_surface_t *thumbnail) { GtkStyleContext *context; context = gtk_widget_get_style_context (widget); - gtk_render_icon (context, cr, thumbnail, 0, 0); + gtk_render_icon_surface (context, cr, thumbnail, 0, 0); return FALSE; } static gboolean applet_enter_notify_event (WnckTasklist *tl, GList *wnck_windows, TasklistData *tasklist) { - GdkPixbuf *thumbnail; + cairo_surface_t *thumbnail; WnckWindow *wnck_window = NULL; int n_windows; + int thumbnail_width; + int thumbnail_height; + int thumbnail_scale; if (tasklist->preview != NULL) { @@ -298,7 +476,7 @@ static gboolean applet_enter_notify_event (WnckTasklist *tl, GList *wnck_windows wnck_screen_get_active_workspace (wnck_screen_get_default ()))) return FALSE; - thumbnail = preview_window_thumbnail (wnck_window, tasklist); + thumbnail = preview_window_thumbnail (wnck_window, tasklist, &thumbnail_width, &thumbnail_height, &thumbnail_scale); if (thumbnail == NULL) return FALSE; @@ -307,13 +485,16 @@ static gboolean applet_enter_notify_event (WnckTasklist *tl, GList *wnck_windows tasklist->preview = gtk_window_new (GTK_WINDOW_POPUP); gtk_widget_set_app_paintable (tasklist->preview, TRUE); + gtk_window_set_default_size (GTK_WINDOW (tasklist->preview), thumbnail_width/thumbnail_scale, thumbnail_height/thumbnail_scale); gtk_window_set_resizable (GTK_WINDOW (tasklist->preview), TRUE); - - preview_window_reposition (tasklist, thumbnail); + preview_window_reposition (tl, tasklist, thumbnail_width/thumbnail_scale, thumbnail_height/thumbnail_scale, thumbnail_scale); gtk_widget_show (tasklist->preview); - g_signal_connect_data (G_OBJECT (tasklist->preview), "draw", G_CALLBACK (preview_window_draw), thumbnail, (GClosureNotify) G_CALLBACK (g_object_unref), 0); + g_signal_connect_data (tasklist->preview, "draw", + G_CALLBACK (preview_window_draw), thumbnail, + (GClosureNotify) G_CALLBACK (cairo_surface_destroy), + 0); return FALSE; } @@ -328,7 +509,8 @@ static gboolean applet_leave_notify_event (WnckTasklist *tl, GList *wnck_windows return FALSE; } -#endif +#endif /* HAVE_WINDOW_PREVIEWS */ +#endif /* HAVE_X11 */ static void applet_change_pixel_size(MatePanelApplet* applet, gint size, TasklistData* tasklist) { @@ -401,9 +583,7 @@ static void tasklist_properties_update_content_radio(TasklistData* tasklist) if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); - gtk_widget_set_sensitive(tasklist->minimized_windows_label, tasklist->include_all_workspaces); - gtk_widget_set_sensitive(tasklist->move_minimized_radio, tasklist->include_all_workspaces); - gtk_widget_set_sensitive(tasklist->change_workspace_radio, tasklist->include_all_workspaces); + gtk_widget_set_sensitive(tasklist->minimized_windows_box, tasklist->include_all_workspaces); } static void display_all_workspaces_changed(GSettings* settings, gchar* key, TasklistData* tasklist) @@ -431,6 +611,11 @@ static void tasklist_update_thumbnail_size_spin(TasklistData* tasklist) gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), (gdouble)tasklist->thumbnail_size); } +static void show_thumbnails_changed(GSettings* settings, gchar* key, TasklistData* tasklist) +{ + tasklist->show_window_thumbnails = g_settings_get_boolean (settings, key); +} + static void thumbnail_size_changed(GSettings *settings, gchar* key, TasklistData* tasklist) { tasklist->thumbnail_size = g_settings_get_int(settings, key); @@ -438,18 +623,18 @@ static void thumbnail_size_changed(GSettings *settings, gchar* key, TasklistData } #endif -static GtkWidget* get_grouping_button(TasklistData* tasklist, WnckTasklistGroupingType type) +static GtkWidget* get_grouping_button(TasklistData* tasklist, TasklistGroupingType type) { switch (type) { default: - case WNCK_TASKLIST_NEVER_GROUP: + case TASKLIST_NEVER_GROUP: return tasklist->never_group_radio; break; - case WNCK_TASKLIST_AUTO_GROUP: + case TASKLIST_AUTO_GROUP: return tasklist->auto_group_radio; break; - case WNCK_TASKLIST_ALWAYS_GROUP: + case TASKLIST_ALWAYS_GROUP: return tasklist->always_group_radio; break; } @@ -457,7 +642,7 @@ static GtkWidget* get_grouping_button(TasklistData* tasklist, WnckTasklistGroupi static void group_windows_changed(GSettings* settings, gchar* key, TasklistData* tasklist) { - WnckTasklistGroupingType type; + TasklistGroupingType type; GtkWidget* button; type = g_settings_get_enum (settings, key); @@ -493,7 +678,6 @@ static void tasklist_update_unminimization_radio(TasklistData* tasklist) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); } - static void move_unminimized_windows_changed(GSettings* settings, gchar* key, TasklistData* tasklist) { gboolean value; @@ -506,6 +690,18 @@ static void move_unminimized_windows_changed(GSettings* settings, gchar* key, Ta tasklist_update_unminimization_radio(tasklist); } +static void scroll_enabled_changed (GSettings* settings, gchar* key, TasklistData* tasklist) +{ + tasklist->scroll_enable = g_settings_get_boolean (settings, key); + tasklist_update(tasklist); +} + +static void middle_click_close_changed (GSettings* settings, gchar* key, TasklistData* tasklist) +{ + tasklist->middle_click_close = g_settings_get_boolean (settings, key); + tasklist_update(tasklist); +} + static void setup_gsettings(TasklistData* tasklist) { tasklist->settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (tasklist->applet), WINDOW_LIST_SCHEMA); @@ -519,6 +715,11 @@ static void setup_gsettings(TasklistData* tasklist) tasklist->preview_settings = mate_panel_applet_settings_new (MATE_PANEL_APPLET (tasklist->applet), WINDOW_LIST_PREVIEW_SCHEMA); g_signal_connect (tasklist->preview_settings, + "changed::show-window-thumbnails", + G_CALLBACK (show_thumbnails_changed), + tasklist); + + g_signal_connect (tasklist->preview_settings, "changed::thumbnail-window-size", G_CALLBACK (thumbnail_size_changed), tasklist); @@ -531,6 +732,14 @@ static void setup_gsettings(TasklistData* tasklist) "changed::move-unminimized-windows", G_CALLBACK (move_unminimized_windows_changed), tasklist); + g_signal_connect (tasklist->settings, + "changed::scroll-enabled", + G_CALLBACK (scroll_enabled_changed), + tasklist); + g_signal_connect (tasklist->settings, + "changed::middle-click-close", + G_CALLBACK (middle_click_close_changed), + tasklist); } static void applet_size_allocate(GtkWidget *widget, GtkAllocation *allocation, TasklistData *tasklist) @@ -538,7 +747,7 @@ static void applet_size_allocate(GtkWidget *widget, GtkAllocation *allocation, T int len; const int* size_hints; - size_hints = wnck_tasklist_get_size_hint_list (WNCK_TASKLIST (tasklist->tasklist), &len); + size_hints = tasklist_get_size_hint_list (tasklist, &len); g_assert(len % 2 == 0); @@ -565,52 +774,6 @@ static void applet_size_allocate(GtkWidget *widget, GtkAllocation *allocation, T mate_panel_applet_set_size_hints(MATE_PANEL_APPLET(tasklist->applet), size_hints, len, 0); } -static GdkPixbuf* icon_loader_func(const char* icon, int size, unsigned int flags, void* data) -{ - TasklistData* tasklist; - GdkPixbuf* retval; - char* icon_no_extension; - char* p; - - tasklist = data; - - if (icon == NULL || strcmp(icon, "") == 0) - return NULL; - - if (g_path_is_absolute(icon)) - { - if (g_file_test(icon, G_FILE_TEST_EXISTS)) - { - return gdk_pixbuf_new_from_file_at_size(icon, size, size, NULL); - } - else - { - char* basename; - - basename = g_path_get_basename(icon); - retval = icon_loader_func(basename, size, flags, data); - g_free(basename); - - return retval; - } - } - - /* This is needed because some .desktop files have an icon name *and* - * an extension as icon */ - icon_no_extension = g_strdup(icon); - p = strrchr(icon_no_extension, '.'); - - if (p && (strcmp(p, ".png") == 0 || strcmp(p, ".xpm") == 0 || strcmp(p, ".svg") == 0)) - { - *p = 0; - } - - retval = gtk_icon_theme_load_icon(tasklist->icon_theme, icon_no_extension, size, 0, NULL); - g_free(icon_no_extension); - - return retval; -} - gboolean window_list_applet_fill(MatePanelApplet* applet) { TasklistData* tasklist; @@ -651,6 +814,10 @@ gboolean window_list_applet_fill(MatePanelApplet* applet) tasklist->move_unminimized_windows = g_settings_get_boolean (tasklist->settings, "move-unminimized-windows"); + tasklist->scroll_enable = g_settings_get_boolean (tasklist->settings, "scroll-enabled"); + + tasklist->middle_click_close = g_settings_get_boolean (tasklist->settings, "middle-click-close"); + tasklist->size = mate_panel_applet_get_size(applet); #if !defined(WNCKLET_INPROCESS) && !GTK_CHECK_VERSION (3, 23, 0) @@ -670,38 +837,64 @@ gboolean window_list_applet_fill(MatePanelApplet* applet) break; } - tasklist->tasklist = wnck_tasklist_new(); - - wnck_tasklist_set_orientation (WNCK_TASKLIST (tasklist->tasklist), tasklist->orientation); - wnck_tasklist_set_middle_click_close (WNCK_TASKLIST (tasklist->tasklist), TRUE); - wnck_tasklist_set_icon_loader(WNCK_TASKLIST(tasklist->tasklist), icon_loader_func, tasklist, NULL); +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + tasklist->tasklist = wnck_tasklist_new(); - g_signal_connect(G_OBJECT(tasklist->tasklist), "destroy", G_CALLBACK(destroy_tasklist), tasklist); #ifdef HAVE_WINDOW_PREVIEWS - g_signal_connect(G_OBJECT(tasklist->tasklist), "task_enter_notify", G_CALLBACK(applet_enter_notify_event), tasklist); - g_signal_connect(G_OBJECT(tasklist->tasklist), "task_leave_notify", G_CALLBACK(applet_leave_notify_event), tasklist); -#endif + g_signal_connect (tasklist->tasklist, "task-enter-notify", + G_CALLBACK (applet_enter_notify_event), + tasklist); + g_signal_connect (tasklist->tasklist, "task-leave-notify", + G_CALLBACK (applet_leave_notify_event), + tasklist); +#endif /* HAVE_WINDOW_PREVIEWS */ + } + else +#endif /* HAVE_X11 */ - g_signal_connect(G_OBJECT(tasklist->applet), "size_allocate", G_CALLBACK(applet_size_allocate), tasklist); +#ifdef HAVE_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + { + tasklist->tasklist = wayland_tasklist_new(); + } + else +#endif /* HAVE_WAYLAND */ - gtk_container_add(GTK_CONTAINER(tasklist->applet), tasklist->tasklist); + { + tasklist->tasklist = gtk_label_new ("[Tasklist not supported on this platform]"); + } + + tasklist_apply_orientation(tasklist); + + g_signal_connect (tasklist->tasklist, "destroy", + G_CALLBACK (destroy_tasklist), + tasklist); + g_signal_connect (tasklist->applet, "size-allocate", + G_CALLBACK (applet_size_allocate), + tasklist); - g_signal_connect(G_OBJECT(tasklist->applet), "realize", G_CALLBACK(applet_realized), tasklist); - g_signal_connect(G_OBJECT(tasklist->applet), "change_orient", G_CALLBACK(applet_change_orient), tasklist); - g_signal_connect(G_OBJECT(tasklist->applet), "change_size", G_CALLBACK(applet_change_pixel_size), tasklist); - g_signal_connect(G_OBJECT(tasklist->applet), "change_background", G_CALLBACK(applet_change_background), tasklist); + gtk_container_add(GTK_CONTAINER(tasklist->applet), tasklist->tasklist); - mate_panel_applet_set_background_widget(MATE_PANEL_APPLET(tasklist->applet), GTK_WIDGET(tasklist->applet)); + g_signal_connect (tasklist->applet, "change-orient", + G_CALLBACK (applet_change_orient), + tasklist); + g_signal_connect (tasklist->applet, "change-size", + G_CALLBACK (applet_change_pixel_size), + tasklist); + g_signal_connect (tasklist->applet, "change-background", + G_CALLBACK(applet_change_background), + tasklist); action_group = gtk_action_group_new("Tasklist Applet Actions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions(action_group, tasklist_menu_actions, G_N_ELEMENTS(tasklist_menu_actions), tasklist); - /* disable the item of system monitor, if not exists. * example, mate-system-monitor, o gnome-system-monitor */ char* programpath; - int i; + gsize i; for (i = 0; i < G_N_ELEMENTS(system_monitors); i += 1) { @@ -723,7 +916,6 @@ gboolean window_list_applet_fill(MatePanelApplet* applet) _system_monitor_found:; /* end of system monitor item */ - mate_panel_applet_setup_menu_from_resource (MATE_PANEL_APPLET (tasklist->applet), WNCKLET_RESOURCE_PATH "window-list-menu.xml", action_group); @@ -747,12 +939,11 @@ gboolean window_list_applet_fill(MatePanelApplet* applet) static void call_system_monitor(GtkAction* action, TasklistData* tasklist) { - char *programpath; - int i; + gsize i; for (i = 0; i < G_N_ELEMENTS(system_monitors); i += 1) { - programpath = g_find_program_in_path(system_monitors[i]); + char *programpath = g_find_program_in_path(system_monitors[i]); if (programpath != NULL) { @@ -766,7 +957,6 @@ static void call_system_monitor(GtkAction* action, TasklistData* tasklist) } } - static void display_help_dialog(GtkAction* action, TasklistData* tasklist) { wncklet_display_help(tasklist->applet, "mate-user-guide", "windowlist", WINDOW_LIST_ICON); @@ -794,13 +984,13 @@ static void display_about_dialog(GtkAction* action, TasklistData* tasklist) "comments", _("The Window List shows a list of all windows in a set of buttons and lets you browse them."), "copyright", _("Copyright \xc2\xa9 2002 Red Hat, Inc.\n" "Copyright \xc2\xa9 2011 Perberos\n" - "Copyright \xc2\xa9 2012-2020 MATE developers"), + "Copyright \xc2\xa9 2012-2021 MATE developers"), "documenters", documenters, "icon-name", WINDOW_LIST_ICON, "logo-icon-name", WINDOW_LIST_ICON, "translator-credits", _("translator-credits"), "version", VERSION, - "website", "http://www.mate-desktop.org/", + "website", PACKAGE_URL, NULL); } @@ -861,13 +1051,26 @@ static void setup_sensitivity(TasklistData* tasklist, GtkBuilder* builder, const } } +#ifdef HAVE_WAYLAND +static void setup_dialog_wayland(TasklistData* tasklist) +{ + gtk_widget_show(tasklist->wayland_info_label); + + gtk_widget_set_sensitive(tasklist->window_list_content_box, FALSE); + gtk_widget_set_sensitive(tasklist->window_grouping_box, FALSE); + gtk_widget_set_sensitive(tasklist->minimized_windows_box, FALSE); + +#ifdef HAVE_WINDOW_PREVIEWS + gtk_widget_set_sensitive(tasklist->window_thumbnail_box, FALSE); +#endif /* HAVE_WINDOW_PREVIEWS */ +} +#endif /* HAVE_WAYLAND */ + static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist) { GtkWidget* button; -#ifdef HAVE_WINDOW_PREVIEWS - GtkAdjustment *adjustment; -#endif + tasklist->wayland_info_label = WID("wayland_info_label"); tasklist->show_current_radio = WID("show_current_radio"); tasklist->show_all_radio = WID("show_all_radio"); @@ -880,6 +1083,7 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist) setup_sensitivity(tasklist, builder, "never_group_radio", "auto_group_radio", "always_group_radio", "group-windows" /* key */); #ifdef HAVE_WINDOW_PREVIEWS + tasklist->window_thumbnail_box = WID("window_thumbnail_box"); tasklist->show_thumbnails_check = WID("show_thumbnails_check"); tasklist->thumbnail_size_label = WID("thumbnail_size_label"); tasklist->thumbnail_size_spin = WID("thumbnail_size_spin"); @@ -895,17 +1099,17 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist) g_object_bind_property(tasklist->show_thumbnails_check, "active", tasklist->thumbnail_size_label, "sensitive", G_BINDING_DEFAULT); g_object_bind_property(tasklist->show_thumbnails_check, "active", tasklist->thumbnail_size_spin, "sensitive", G_BINDING_DEFAULT); - adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON(tasklist->thumbnail_size_spin)); - gtk_adjustment_set_lower (adjustment, 0); - gtk_adjustment_set_upper (adjustment, 999); - gtk_adjustment_set_step_increment (adjustment, 1); #else - gtk_widget_hide(WID("window_thumbnails")); + gtk_widget_hide(WID("window_thumbnail_box")); #endif - tasklist->minimized_windows_label = WID("minimized_windows_label"); tasklist->move_minimized_radio = WID("move_minimized_radio"); tasklist->change_workspace_radio = WID("change_workspace_radio"); + tasklist->mouse_scroll_check = WID("mouse_scroll_check"); + tasklist->middle_click_close_check = WID("middle_click_close_check"); + tasklist->minimized_windows_box = WID("minimized_windows_box"); + tasklist->window_grouping_box = WID("window_grouping_box"); + tasklist->window_list_content_box = WID("window_list_content_box"); setup_sensitivity(tasklist, builder, "move_minimized_radio", "change_workspace_radio", NULL, "move-unminimized-windows" /* key */); @@ -916,26 +1120,62 @@ static void setup_dialog(GtkBuilder* builder, TasklistData* tasklist) g_object_set_data(G_OBJECT(tasklist->auto_group_radio), "group_value", "auto"); g_object_set_data(G_OBJECT(tasklist->always_group_radio), "group_value", "always"); - g_signal_connect(G_OBJECT(tasklist->never_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist); - g_signal_connect(G_OBJECT(tasklist->auto_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist); - g_signal_connect(G_OBJECT(tasklist->always_group_radio), "toggled", (GCallback) group_windows_toggled, tasklist); + g_signal_connect (tasklist->never_group_radio, "toggled", + (GCallback) group_windows_toggled, + tasklist); + g_signal_connect (tasklist->auto_group_radio, "toggled", + (GCallback) group_windows_toggled, + tasklist); + g_signal_connect (tasklist->always_group_radio, "toggled", + (GCallback) group_windows_toggled, + tasklist); + + /* Mouse Scroll: */ + g_settings_bind (tasklist->settings, + "scroll-enabled", + tasklist->mouse_scroll_check, + "active", + G_SETTINGS_BIND_DEFAULT); + + /* Middle mouse click to close window: */ + g_settings_bind (tasklist->settings, + "middle-click-close", + tasklist->middle_click_close_check, + "active", + G_SETTINGS_BIND_DEFAULT); #ifdef HAVE_WINDOW_PREVIEWS /* change thumbnail size: */ tasklist_update_thumbnail_size_spin(tasklist); - g_signal_connect(G_OBJECT(tasklist->thumbnail_size_spin), "value-changed", (GCallback) thumbnail_size_spin_changed, tasklist); + g_signal_connect (tasklist->thumbnail_size_spin, "value-changed", + (GCallback) thumbnail_size_spin_changed, + tasklist); #endif /* move window when unminimizing: */ tasklist_update_unminimization_radio(tasklist); - g_signal_connect(G_OBJECT(tasklist->move_minimized_radio), "toggled", (GCallback) move_minimized_toggled, tasklist); + g_signal_connect (tasklist->move_minimized_radio, "toggled", + (GCallback) move_minimized_toggled, + tasklist); /* Tasklist content: */ tasklist_properties_update_content_radio (tasklist); - g_signal_connect(G_OBJECT(tasklist->show_all_radio), "toggled", (GCallback) display_all_workspaces_toggled, tasklist); - - g_signal_connect_swapped(WID("done_button"), "clicked", (GCallback) gtk_widget_hide, tasklist->properties_dialog); - g_signal_connect(tasklist->properties_dialog, "response", G_CALLBACK(response_cb), tasklist); + g_signal_connect (tasklist->show_all_radio, "toggled", + (GCallback) display_all_workspaces_toggled, + tasklist); + + g_signal_connect_swapped (WID ("done_button"), "clicked", + (GCallback) gtk_widget_hide, + tasklist->properties_dialog); + g_signal_connect (tasklist->properties_dialog, "response", + G_CALLBACK (response_cb), + tasklist); + +#ifdef HAVE_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { + setup_dialog_wayland(tasklist); + } +#endif /* HAVE_WAYLAND */ } static void display_properties_dialog(GtkAction* action, TasklistData* tasklist) diff --git a/applets/wncklet/window-list.ui b/applets/wncklet/window-list.ui index d780d386..4cc49f9c 100644 --- a/applets/wncklet/window-list.ui +++ b/applets/wncklet/window-list.ui @@ -1,41 +1,46 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.36.0 --> +<!-- Generated with glade 3.38.2 --> <interface> <requires lib="gtk+" version="3.22"/> + <object class="GtkAdjustment" id="adjustment1"> + <property name="upper">999</property> + <property name="step-increment">1</property> + <property name="page-increment">10</property> + </object> <object class="GtkImage" id="image1"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">help-browser</property> + <property name="can-focus">False</property> + <property name="icon-name">help-browser</property> </object> <object class="GtkImage" id="image2"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">window-close</property> + <property name="can-focus">False</property> + <property name="icon-name">window-close</property> </object> <object class="GtkDialog" id="tasklist_properties_dialog"> - <property name="can_focus">False</property> - <property name="border_width">5</property> + <property name="can-focus">False</property> + <property name="border-width">5</property> <property name="title" translatable="yes">Window List Preferences</property> - <property name="type_hint">normal</property> + <property name="resizable">False</property> + <property name="icon-name">mate-panel-window-list</property> + <property name="type-hint">normal</property> <child internal-child="vbox"> - <object class="GtkBox" id="dialog-vbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> + <object class="GtkBox"> + <property name="can-focus">False</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child internal-child="action_area"> <object class="GtkButtonBox" id="dialog-action_area2"> - <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <child> <object class="GtkButton" id="help_button"> <property name="label" translatable="yes">_Help</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="can-default">True</property> + <property name="receives-default">False</property> <property name="image">image1</property> - <property name="use_underline">True</property> + <property name="use-underline">True</property> </object> <packing> <property name="expand">False</property> @@ -47,12 +52,12 @@ <object class="GtkButton" id="done_button"> <property name="label" translatable="yes">_Close</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="can-default">True</property> + <property name="has-default">True</property> + <property name="receives-default">False</property> <property name="image">image2</property> - <property name="use_underline">True</property> + <property name="use-underline">True</property> </object> <packing> <property name="expand">False</property> @@ -64,149 +69,206 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">1</property> + <property name="position">0</property> </packing> </child> <child> - <object class="GtkBox" id="vbox1"> + <object class="GtkNotebook"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="border_width">5</property> - <property name="orientation">vertical</property> - <property name="spacing">18</property> + <property name="can-focus">True</property> <child> - <object class="GtkBox" id="vbox7"> + <object class="GtkBox" id="behaviour_vbox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="border-width">12</property> <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Window List Content</property> - <property name="xalign">0</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> + <property name="spacing">18</property> <child> - <object class="GtkAlignment" id="alignment1"> + <object class="GtkBox" id="window_thumbnail_box"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="left_padding">12</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkBox" id="vbox9"> + <object class="GtkLabel" id="thumbnails_label"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Window Thumbnails</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="vbox16"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkRadioButton" id="show_current_radio"> - <property name="label" translatable="yes">Sh_ow windows from current workspace</property> + <object class="GtkCheckButton" id="show_thumbnails_check"> + <property name="label" translatable="yes">Show _thumbnails on hover</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> - <property name="fill">False</property> + <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> - <object class="GtkRadioButton" id="show_all_radio"> - <property name="label" translatable="yes">Show windows from a_ll workspaces</property> + <object class="GtkBox" id="thumbnail_box"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">show_current_radio</property> + <property name="can-focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="thumbnail_size_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Thumbnail width in pixels. Window aspect ratio will be maintained.</property> + <property name="label" translatable="yes">Thumbnail width:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="thumbnail_size_spin"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="max-length">3</property> + <property name="text" translatable="yes">0</property> + <property name="caps-lock-warning">False</property> + <property name="placeholder-text" translatable="yes">px</property> + <property name="input-purpose">number</property> + <property name="adjustment">adjustment1</property> + <property name="climb-rate">1</property> + <property name="numeric">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> - <property name="fill">False</property> + <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkBox" id="window_thumbnails"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Window Thumbnails</property> - <property name="xalign">0</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> <property name="position">0</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment4"> + <object class="GtkBox" id="window_grouping_box"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="left_padding">12</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkBox" id="vbox16"> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Window Grouping</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="vbox12"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkCheckButton" id="show_thumbnails_check"> - <property name="label" translatable="yes">Show _thumbnails on hover</property> + <object class="GtkRadioButton" id="never_group_radio"> + <property name="label" translatable="yes">_Never group windows</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> - <property name="fill">True</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="auto_group_radio"> + <property name="label" translatable="yes">Group windows when _space is limited</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <property name="group">never_group_radio</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> <property name="position">1</property> </packing> </child> + <child> + <object class="GtkRadioButton" id="always_group_radio"> + <property name="label" translatable="yes">_Always group windows</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <property name="group">never_group_radio</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> </child> </object> <packing> @@ -216,22 +278,42 @@ </packing> </child> <child> - <object class="GtkAlignment" id="alignment5"> + <object class="GtkBox" id="middle_click_close_box"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="left_padding">12</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkBox" id="hbox1"> + <object class="GtkLabel" id="middle_click_close_label"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Middle mouse button</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="middle_click_close_vbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> + <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkLabel" id="thumbnail_size_label"> + <object class="GtkCheckButton" id="middle_click_close_check"> + <property name="label" translatable="yes">_Click to close window</property> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Thumbnail width in pixels. Window aspect ratio will be maintained.</property> - <property name="label" translatable="yes">Thumbnail width:</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> @@ -239,82 +321,135 @@ <property name="position">0</property> </packing> </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="mouse_scroll_box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="mouse_scrolling_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Mouse Scrolling</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="mouse_scrolling_vbox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkSpinButton" id="thumbnail_size_spin"> + <object class="GtkCheckButton" id="mouse_scroll_check"> + <property name="label" translatable="yes">_Enable mouse scrolling</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">3</property> - <property name="caps_lock_warning">False</property> - <property name="placeholder_text" translatable="yes">px</property> - <property name="input_purpose">number</property> - <property name="climb_rate">1</property> - <property name="numeric">True</property> - <property name="value">200</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">1</property> + <property name="position">0</property> </packing> </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="behaviour_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Behaviour</property> + </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="tab-fill">False</property> </packing> </child> <child> - <object class="GtkBox" id="vbox11"> + <object class="GtkBox" id="workspaces_vbox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="border-width">12</property> <property name="orientation">vertical</property> - <property name="spacing">6</property> + <property name="spacing">18</property> <child> - <object class="GtkLabel" id="label3"> + <object class="GtkBox" id="window_list_content_box"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Window Grouping</property> - <property name="xalign">0</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkAlignment" id="alignment2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="left_padding">12</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkBox" id="vbox12"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Window List Content</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="vbox9"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkRadioButton" id="never_group_radio"> - <property name="label" translatable="yes">_Never group windows</property> + <object class="GtkRadioButton" id="show_current_radio"> + <property name="label" translatable="yes">Sh_ow windows from current workspace</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> @@ -323,14 +458,14 @@ </packing> </child> <child> - <object class="GtkRadioButton" id="auto_group_radio"> - <property name="label" translatable="yes">Group windows when _space is limited</property> + <object class="GtkRadioButton" id="show_all_radio"> + <property name="label" translatable="yes">Show windows from a_ll workspaces</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">never_group_radio</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <property name="group">show_current_radio</property> </object> <packing> <property name="expand">False</property> @@ -338,80 +473,58 @@ <property name="position">1</property> </packing> </child> - <child> - <object class="GtkRadioButton" id="always_group_radio"> - <property name="label" translatable="yes">_Always group windows</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">never_group_radio</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkBox" id="vbox13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="minimized_windows_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Restoring Minimized Windows</property> - <property name="xalign">0</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> <property name="position">0</property> </packing> </child> <child> - <object class="GtkAlignment" id="alignment3"> + <object class="GtkBox" id="minimized_windows_box"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="left_padding">12</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="minimized_windows_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Restoring Minimized Windows</property> + <property name="xalign">0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> <child> <object class="GtkBox" id="vbox14"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> + <property name="margin-start">6</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> <object class="GtkRadioButton" id="move_minimized_radio"> <property name="label" translatable="yes">Restore to current _workspace</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> </object> <packing> <property name="expand">False</property> @@ -423,10 +536,10 @@ <object class="GtkRadioButton" id="change_workspace_radio"> <property name="label" translatable="yes">Restore to na_tive workspace</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <property name="group">move_minimized_radio</property> </object> <packing> @@ -436,6 +549,11 @@ </packing> </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> </child> </object> <packing> @@ -446,9 +564,18 @@ </child> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="workspaces_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Workspaces</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab-fill">False</property> </packing> </child> </object> @@ -462,10 +589,7 @@ </child> <action-widgets> <action-widget response="-11">help_button</action-widget> - <action-widget response="0">done_button</action-widget> + <action-widget response="-7">done_button</action-widget> </action-widgets> - <child type="titlebar"> - <placeholder/> - </child> </object> </interface> diff --git a/applets/wncklet/window-menu.c b/applets/wncklet/window-menu.c index dbe09f05..4b4e48dc 100644 --- a/applets/wncklet/window-menu.c +++ b/applets/wncklet/window-menu.c @@ -26,7 +26,7 @@ */ #ifdef HAVE_CONFIG_H - #include <config.h> +#include <config.h> #endif #include <string.h> @@ -35,8 +35,15 @@ #include <glib/gi18n.h> #include <gdk/gdkkeysyms.h> +#ifdef HAVE_X11 +#include <gdk/gdkx.h> #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include <libwnck/libwnck.h> +#endif /* HAVE_X11 */ + +#ifdef HAVE_WAYLAND +#include <gdk/gdkwayland.h> +#endif /* HAVE_WAYLAND */ #include "wncklet.h" #include "window-menu.h" @@ -81,13 +88,13 @@ static void window_menu_about(GtkAction* action, WindowMenu* window_menu) "Copyright \xc2\xa9 2001 Free Software Foundation, Inc.\n" "Copyright \xc2\xa9 2003 Sun Microsystems, Inc.\n" "Copyright \xc2\xa9 2011 Perberos\n" - "Copyright \xc2\xa9 2012-2020 MATE developers"), + "Copyright \xc2\xa9 2012-2021 MATE developers"), "documenters", documenters, "icon-name", WINDOW_MENU_ICON, "logo-icon-name", WINDOW_MENU_ICON, "translator-credits", _("translator-credits"), "version", VERSION, - "website", "http://www.mate-desktop.org/", + "website", PACKAGE_URL, NULL); } @@ -151,6 +158,9 @@ static void window_menu_size_allocate(MatePanelApplet* applet, GtkAllocation* al orient = mate_panel_applet_get_orient(applet); + if (!GTK_IS_CONTAINER (window_menu->selector)) + return; + children = gtk_container_get_children(GTK_CONTAINER(window_menu->selector)); child = GTK_WIDGET(children->data); g_list_free(children); @@ -178,7 +188,6 @@ static void window_menu_size_allocate(MatePanelApplet* applet, GtkAllocation* al static gboolean window_menu_key_press_event(GtkWidget* widget, GdkEventKey* event, WindowMenu* window_menu) { GtkMenuShell* menu_shell; - WnckSelector* selector; switch (event->keyval) { @@ -188,7 +197,6 @@ static gboolean window_menu_key_press_event(GtkWidget* widget, GdkEventKey* even case GDK_KEY_Return: case GDK_KEY_space: case GDK_KEY_KP_Space: - selector = WNCK_SELECTOR(window_menu->selector); /* * We need to call _gtk_menu_shell_activate() here as is done in * window_key_press_handler in gtkmenubar.c which pops up menu @@ -196,7 +204,7 @@ static gboolean window_menu_key_press_event(GtkWidget* widget, GdkEventKey* even * * As that function is private its code is replicated here. */ - menu_shell = GTK_MENU_SHELL(selector); + menu_shell = GTK_MENU_SHELL(window_menu->selector); gtk_menu_shell_select_first(menu_shell, FALSE); return TRUE; @@ -210,7 +218,7 @@ static gboolean window_menu_key_press_event(GtkWidget* widget, GdkEventKey* even static gboolean filter_button_press(GtkWidget* widget, GdkEventButton* event, gpointer data) { if (event->button != 1) - g_signal_stop_emission_by_name(widget, "button_press_event"); + g_signal_stop_emission_by_name(widget, "button-press-event"); return FALSE; } @@ -231,6 +239,7 @@ gboolean window_menu_applet_fill(MatePanelApplet* applet) window_menu->orient = mate_panel_applet_get_orient(applet); g_signal_connect(window_menu->applet, "destroy", G_CALLBACK(window_menu_destroy), window_menu); + g_signal_connect(window_menu->applet, "key-press-event", G_CALLBACK(window_menu_key_press_event), window_menu); action_group = gtk_action_group_new("WindowMenu Applet Actions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); @@ -240,19 +249,45 @@ gboolean window_menu_applet_fill(MatePanelApplet* applet) action_group); g_object_unref(action_group); - window_menu->selector = wnck_selector_new(); - gtk_container_add(GTK_CONTAINER(window_menu->applet), window_menu->selector); +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + window_menu->selector = wnck_selector_new(); + } + else +#endif /* HAVE_X11 */ - mate_panel_applet_set_background_widget(MATE_PANEL_APPLET(window_menu->applet), GTK_WIDGET(window_menu->selector)); +#ifdef HAVE_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + { + window_menu->selector = gtk_label_new ("[Window menu not supported on Wayland]"); + } + else +#endif /* HAVE_WAYLAND */ - g_signal_connect(window_menu->applet, "key_press_event", G_CALLBACK(window_menu_key_press_event), window_menu); - g_signal_connect(window_menu->applet, "size-allocate", G_CALLBACK(window_menu_size_allocate), window_menu); + { + window_menu->selector = gtk_label_new ("[Window menu not supported on this platform]"); + } - g_signal_connect_after(G_OBJECT(window_menu->applet), "focus-in-event", G_CALLBACK(gtk_widget_queue_draw), window_menu); - g_signal_connect_after(G_OBJECT(window_menu->applet), "focus-out-event", G_CALLBACK(gtk_widget_queue_draw), window_menu); - g_signal_connect_after(G_OBJECT(window_menu->selector), "draw", G_CALLBACK(window_menu_on_draw), window_menu); + gtk_container_add(GTK_CONTAINER(window_menu->applet), window_menu->selector); - g_signal_connect(G_OBJECT(window_menu->selector), "button_press_event", G_CALLBACK(filter_button_press), window_menu); + g_signal_connect (window_menu->applet, "size-allocate", + G_CALLBACK(window_menu_size_allocate), + window_menu); + + g_signal_connect_after (window_menu->applet, "focus-in-event", + G_CALLBACK (gtk_widget_queue_draw), + window_menu); + g_signal_connect_after (window_menu->applet, "focus-out-event", + G_CALLBACK (gtk_widget_queue_draw), + window_menu); + g_signal_connect_after (window_menu->selector, "draw", + G_CALLBACK (window_menu_on_draw), + window_menu); + + g_signal_connect (window_menu->selector, "button_press_event", + G_CALLBACK (filter_button_press), + window_menu); gtk_widget_show_all(GTK_WIDGET(window_menu->applet)); diff --git a/applets/wncklet/wncklet.c b/applets/wncklet/wncklet.c index c24b41f0..3b185910 100644 --- a/applets/wncklet/wncklet.c +++ b/applets/wncklet/wncklet.c @@ -25,18 +25,17 @@ #include <config.h> #endif -#ifndef HAVE_X11 -#error file should only be built when HAVE_X11 is enabled -#endif - #include <string.h> #include <mate-panel-applet.h> #include <glib/gi18n.h> #include <gtk/gtk.h> + +#ifdef HAVE_X11 #include <gdk/gdkx.h> #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include <libwnck/libwnck.h> +#endif #include "wncklet.h" #include "window-menu.h" @@ -96,8 +95,11 @@ void wncklet_display_help(GtkWidget* widget, const char* doc_id, const char* lin } } +#ifdef HAVE_X11 WnckScreen* wncklet_get_screen(GtkWidget* applet) { + g_return_val_if_fail (GDK_IS_X11_DISPLAY (gdk_display_get_default ()), NULL); + int screen_num; if (!gtk_widget_has_screen(applet)) @@ -107,6 +109,7 @@ WnckScreen* wncklet_get_screen(GtkWidget* applet) return wnck_screen_get(screen_num); } +#endif /* HAVE_X11 */ void wncklet_connect_while_alive(gpointer object, const char* signal, GCallback func, gpointer func_data, gpointer alive_object) { @@ -120,13 +123,18 @@ void wncklet_connect_while_alive(gpointer object, const char* signal, GCallback static gboolean wncklet_factory(MatePanelApplet* applet, const char* iid, gpointer data) { gboolean retval = FALSE; - static gboolean type_registered = FALSE; - if (!type_registered) +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) { - wnck_set_client_type(WNCK_CLIENT_TYPE_PAGER); - type_registered = TRUE; + static gboolean type_registered = FALSE; + if (!type_registered) + { + wnck_set_client_type(WNCK_CLIENT_TYPE_PAGER); + type_registered = TRUE; + } } +#endif /* HAVE_X11 */ if (!strcmp(iid, "WindowMenuApplet")) retval = window_menu_applet_fill(applet); @@ -140,7 +148,6 @@ static gboolean wncklet_factory(MatePanelApplet* applet, const char* iid, gpoint return retval; } - #ifdef WNCKLET_INPROCESS MATE_PANEL_APPLET_IN_PROCESS_FACTORY("WnckletFactory", PANEL_TYPE_APPLET, "WindowNavigationApplets", wncklet_factory, NULL) #else diff --git a/applets/wncklet/wncklet.h b/applets/wncklet/wncklet.h index d561937f..145cbce3 100644 --- a/applets/wncklet/wncklet.h +++ b/applets/wncklet/wncklet.h @@ -24,8 +24,6 @@ #ifndef __WNCKLET_H__ #define __WNCKLET_H__ -#include <libwnck/libwnck.h> - #include <glib.h> #include <gtk/gtk.h> #include <mate-panel-applet.h> @@ -36,6 +34,8 @@ extern "C" { #endif +typedef struct _WnckScreen WnckScreen; + void wncklet_display_help(GtkWidget* widget, const char* doc_id, const char* link_id, const char* icon_name); WnckScreen* wncklet_get_screen(GtkWidget* applet); diff --git a/applets/wncklet/workspace-switcher.c b/applets/wncklet/workspace-switcher.c index 4d74856b..e3fda355 100644 --- a/applets/wncklet/workspace-switcher.c +++ b/applets/wncklet/workspace-switcher.c @@ -21,9 +21,17 @@ #include <glib/gi18n.h> #include <gtk/gtk.h> +#include <gio/gio.h> + +#ifdef HAVE_X11 +#include <gdk/gdkx.h> #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include <libwnck/libwnck.h> -#include <gio/gio.h> +#endif /* HAVE_X11 */ + +#ifdef HAVE_WAYLAND +#include <gdk/gdkwayland.h> +#endif /* HAVE_WAYLAND */ #include <libmate-desktop/mate-gsettings.h> @@ -45,17 +53,166 @@ #define WORKSPACE_SWITCHER_ICON "mate-panel-workspace-switcher" +/* Container for the WnckPager to work around the sizing issues we have in the + * panel. See + * https://github.com/mate-desktop/mate-panel/issues/1230#issuecomment-1046235088 */ + +typedef struct _PagerContainer PagerContainer; +typedef GtkBinClass PagerContainerClass; + +static GType pager_container_get_type (void); + +#define PAGER_CONTAINER_TYPE (pager_container_get_type ()) +#define PAGER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PAGER_CONTAINER_TYPE, PagerContainer)) + +struct _PagerContainer +{ + GtkBin parent; + GtkOrientation orientation; + int size; +}; + +G_DEFINE_TYPE (PagerContainer, pager_container, GTK_TYPE_BIN) + +static gboolean +queue_resize_idle_cb (gpointer user_data) +{ + gtk_widget_queue_resize (GTK_WIDGET (user_data)); + return G_SOURCE_REMOVE; +} + +static void +pager_container_get_preferred_width (GtkWidget *widget, + int *minimum_width, + int *natural_width) +{ + PagerContainer *self; + + self = PAGER_CONTAINER (widget); + + if (self->orientation == GTK_ORIENTATION_VERTICAL) + { + /* self->size is panel width */ + *minimum_width = *natural_width = self->size; + } + else + { + /* self->size is panel size/height, that will get allocated to pager, request width for this size */ + gtk_widget_get_preferred_width_for_height (gtk_bin_get_child (GTK_BIN (self)), + self->size, + minimum_width, + natural_width); + } +} + +static void +pager_container_get_preferred_height (GtkWidget *widget, + int *minimum_height, + int *natural_height) +{ + PagerContainer *self; + + self = PAGER_CONTAINER (widget); + + if (self->orientation == GTK_ORIENTATION_VERTICAL) + { + /* self->size is panel size/width that will get allocated to pager, request height for this size */ + gtk_widget_get_preferred_height_for_width (gtk_bin_get_child (GTK_BIN (self)), + self->size, + minimum_height, + natural_height); + } + else + { + /* self->size is panel height */ + *minimum_height = *natural_height = self->size; + } +} + +static void +pager_container_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + PagerContainer *self; + int size; + + self = PAGER_CONTAINER (widget); + + if (self->orientation == GTK_ORIENTATION_VERTICAL) + size = allocation->width; + else + size = allocation->height; + + size = MAX (size, 1); + + if (self->size != size) + { + self->size = size; + g_idle_add (queue_resize_idle_cb, self); + return; + } + + GTK_WIDGET_CLASS (pager_container_parent_class)->size_allocate (widget, + allocation); +} + +static void +pager_container_class_init (PagerContainerClass *self_class) +{ + GtkWidgetClass *widget_class; + + widget_class = GTK_WIDGET_CLASS (self_class); + + widget_class->get_preferred_width = pager_container_get_preferred_width; + widget_class->get_preferred_height = pager_container_get_preferred_height; + widget_class->size_allocate = pager_container_size_allocate; +} + +static void +pager_container_init (PagerContainer *self) +{ +} + +static GtkWidget * +pager_container_new (GtkWidget *child, + GtkOrientation orientation) +{ + PagerContainer *self; + + self = g_object_new (PAGER_CONTAINER_TYPE, "child", child, NULL); + + self->orientation = orientation; + + return GTK_WIDGET (self); +} + +static void +pager_container_set_orientation (PagerContainer *self, + GtkOrientation orientation) +{ + if (self->orientation == orientation) + return; + + self->orientation = orientation; + + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +/* Pager applet itself */ + typedef enum { PAGER_WM_MARCO, PAGER_WM_METACITY, PAGER_WM_COMPIZ, PAGER_WM_I3, + PAGER_WM_XMONAD, PAGER_WM_UNKNOWN } PagerWM; typedef struct { GtkWidget* applet; + GtkWidget* pager_container; GtkWidget* pager; WnckScreen* screen; @@ -79,7 +236,7 @@ typedef struct { GtkOrientation orientation; int n_rows; /* for vertical layout this is cols */ - WnckPagerDisplayMode display_mode; + gboolean display_names; /* if to display names or content */ gboolean display_all; gboolean wrap_workspaces; @@ -91,20 +248,35 @@ static void display_help_dialog(GtkAction* action, PagerData* pager); static void display_about_dialog(GtkAction* action, PagerData* pager); static void destroy_pager(GtkWidget* widget, PagerData* pager); -static void pager_update(PagerData* pager) +#ifdef HAVE_X11 +static void pager_update_wnck(PagerData* pager, WnckPager* wnck_pager) { - wnck_pager_set_orientation(WNCK_PAGER(pager->pager), pager->orientation); - wnck_pager_set_n_rows(WNCK_PAGER(pager->pager), pager->n_rows); - wnck_pager_set_show_all(WNCK_PAGER(pager->pager), pager->display_all); + WnckPagerDisplayMode display_mode = WNCK_PAGER_DISPLAY_CONTENT; - if (pager->wm == PAGER_WM_MARCO) - wnck_pager_set_display_mode(WNCK_PAGER(pager->pager), pager->display_mode); - else if (pager->wm == PAGER_WM_METACITY) - wnck_pager_set_display_mode(WNCK_PAGER(pager->pager), pager->display_mode); - else if (pager->wm == PAGER_WM_I3) - wnck_pager_set_display_mode(WNCK_PAGER(pager->pager), pager->display_mode); - else - wnck_pager_set_display_mode(WNCK_PAGER(pager->pager), WNCK_PAGER_DISPLAY_CONTENT); + if (pager->display_names && ( + pager->wm == PAGER_WM_MARCO || + pager->wm == PAGER_WM_METACITY || + pager->wm == PAGER_WM_I3 || + pager->wm == PAGER_WM_XMONAD)) + { + display_mode = WNCK_PAGER_DISPLAY_NAME; + } + + wnck_pager_set_orientation(wnck_pager, pager->orientation); + wnck_pager_set_n_rows(wnck_pager, pager->n_rows); + wnck_pager_set_show_all(wnck_pager, pager->display_all); + wnck_pager_set_display_mode(wnck_pager, display_mode); +} +#endif /* HAVE_X11 */ + +static void pager_update(PagerData* pager) +{ +#ifdef HAVE_X11 + if (WNCK_IS_PAGER(pager->pager)) + { + pager_update_wnck(pager, WNCK_PAGER(pager->pager)); + } +#endif /* HAVE_X11 */ } static void update_properties_for_wm(PagerData* pager) @@ -149,6 +321,20 @@ static void update_properties_for_wm(PagerData* pager) if (pager->cell) g_object_set (pager->cell, "editable", FALSE, NULL); break; + case PAGER_WM_XMONAD: + if (pager->workspaces_frame) + gtk_widget_show(pager->workspaces_frame); + if (pager->num_workspaces_spin) + gtk_widget_set_sensitive(pager->num_workspaces_spin, FALSE); + if (pager->workspace_names_label) + gtk_widget_hide(pager->workspace_names_label); + if (pager->workspace_names_scroll) + gtk_widget_hide(pager->workspace_names_scroll); + if (pager->display_workspaces_toggle) + gtk_widget_show(pager->display_workspaces_toggle); + if (pager->cell) + g_object_set (pager->cell, "editable", FALSE, NULL); + break; case PAGER_WM_COMPIZ: if (pager->workspaces_frame) gtk_widget_show(pager->workspaces_frame); @@ -178,9 +364,13 @@ static void update_properties_for_wm(PagerData* pager) static void window_manager_changed(WnckScreen* screen, PagerData* pager) { - const char *wm_name; +#ifdef HAVE_X11 + const char *wm_name = NULL; - wm_name = wnck_screen_get_window_manager_name(screen); + if (pager->screen) + { + wm_name = wnck_screen_get_window_manager_name (pager->screen); + } if (!wm_name) pager->wm = PAGER_WM_UNKNOWN; @@ -190,10 +380,15 @@ static void window_manager_changed(WnckScreen* screen, PagerData* pager) pager->wm = PAGER_WM_METACITY; else if (strcmp(wm_name, "i3") == 0) pager->wm = PAGER_WM_I3; + else if (strcmp(wm_name, "xmonad") == 0) + pager->wm = PAGER_WM_XMONAD; else if (strcmp(wm_name, "Compiz") == 0) pager->wm = PAGER_WM_COMPIZ; else pager->wm = PAGER_WM_UNKNOWN; +#else + pager->wm = PAGER_WM_UNKNOWN; +#endif /* HAVE_X11 */ update_properties_for_wm(pager); pager_update(pager); @@ -201,15 +396,22 @@ static void window_manager_changed(WnckScreen* screen, PagerData* pager) static void applet_realized(MatePanelApplet* applet, PagerData* pager) { - pager->screen = wncklet_get_screen(GTK_WIDGET(applet)); +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + pager->screen = wncklet_get_screen(GTK_WIDGET(applet)); + wncklet_connect_while_alive(pager->screen, "window_manager_changed", G_CALLBACK(window_manager_changed), pager, pager->applet); + } +#endif /* HAVE_X11 */ window_manager_changed(pager->screen, pager); - wncklet_connect_while_alive(pager->screen, "window_manager_changed", G_CALLBACK(window_manager_changed), pager, pager->applet); } static void applet_unrealized(MatePanelApplet* applet, PagerData* pager) { +#ifdef HAVE_X11 pager->screen = NULL; +#endif /* HAVE_X11 */ pager->wm = PAGER_WM_UNKNOWN; } @@ -236,6 +438,8 @@ static void applet_change_orient(MatePanelApplet* applet, MatePanelAppletOrient pager->orientation = new_orient; pager_update(pager); + pager_container_set_orientation(PAGER_CONTAINER(pager->pager_container), pager->orientation); + if (pager->label_row_col) gtk_label_set_text(GTK_LABEL(pager->label_row_col), pager->orientation == GTK_ORIENTATION_HORIZONTAL ? _("rows") : _("columns")); } @@ -248,8 +452,13 @@ static void applet_change_background(MatePanelApplet* applet, MatePanelAppletBac gtk_style_context_set_path (new_context, gtk_widget_get_path (GTK_WIDGET (pager->pager))); g_object_unref (new_context); - wnck_pager_set_shadow_type (WNCK_PAGER (pager->pager), - type == PANEL_NO_BACKGROUND ? GTK_SHADOW_NONE : GTK_SHADOW_IN); +#ifdef HAVE_X11 + if (WNCK_IS_PAGER(pager->pager)) + { + wnck_pager_set_shadow_type (WNCK_PAGER (pager->pager), + type == PANEL_NO_BACKGROUND ? GTK_SHADOW_NONE : GTK_SHADOW_IN); + } +#endif /* HAVE_X11 */ } static void applet_style_updated (MatePanelApplet *applet, GtkStyleContext *context) @@ -282,11 +491,13 @@ static void applet_style_updated (MatePanelApplet *applet, GtkStyleContext *cont */ static gboolean applet_scroll(MatePanelApplet* applet, GdkEventScroll* event, PagerData* pager) { +#ifdef HAVE_X11 GdkScrollDirection absolute_direction; int index; int n_workspaces; int n_columns; int in_last_row; +#endif /* HAVE_X11 */ if (event->type != GDK_SCROLL) return FALSE; @@ -294,8 +505,18 @@ static gboolean applet_scroll(MatePanelApplet* applet, GdkEventScroll* event, Pa if (event->direction == GDK_SCROLL_SMOOTH) return FALSE; - index = wnck_workspace_get_number(wnck_screen_get_active_workspace(pager->screen)); - n_workspaces = wnck_screen_get_workspace_count(pager->screen); +#ifdef HAVE_X11 + if (pager->screen) + { + index = wnck_workspace_get_number(wnck_screen_get_active_workspace(pager->screen)); + n_workspaces = wnck_screen_get_workspace_count(pager->screen); + } + else + { + index = 0; + n_workspaces = 1; + } + n_columns = n_workspaces / pager->n_rows; if (n_workspaces % pager->n_rows != 0) @@ -381,7 +602,11 @@ static gboolean applet_scroll(MatePanelApplet* applet, GdkEventScroll* event, Pa break; } - wnck_workspace_activate(wnck_screen_get_workspace(pager->screen, index), event->time); + if (pager->screen) + { + wnck_workspace_activate(wnck_screen_get_workspace(pager->screen, index), event->time); + } +#endif /* HAVE_X11 */ return TRUE; } @@ -415,11 +640,12 @@ static const GtkActionEntry pager_menu_actions[] = { static void num_rows_changed(GSettings* settings, gchar* key, PagerData* pager) { - int n_rows = DEFAULT_ROWS; + int n_rows; - n_rows = g_settings_get_int (settings, key); - - n_rows = CLAMP(n_rows, 1, MAX_REASONABLE_ROWS); + n_rows = CLAMP (g_settings_get_int (settings, key), + 1, + MIN (wnck_screen_get_workspace_count (pager->screen), + MAX_REASONABLE_ROWS)); pager->n_rows = n_rows; pager_update(pager); @@ -434,14 +660,7 @@ static void display_workspace_names_changed(GSettings* settings, gchar* key, Pag value = g_settings_get_boolean (settings, key); - if (value) - { - pager->display_mode = WNCK_PAGER_DISPLAY_NAME; - } - else - { - pager->display_mode = WNCK_PAGER_DISPLAY_CONTENT; - } + pager->display_names = g_settings_get_boolean (settings, key); pager_update(pager); @@ -451,12 +670,9 @@ static void display_workspace_names_changed(GSettings* settings, gchar* key, Pag } } - static void all_workspaces_changed(GSettings* settings, gchar* key, PagerData* pager) { - gboolean value = TRUE; /* Default value */ - - value = g_settings_get_boolean (settings, key); + gboolean value = g_settings_get_boolean (settings, key); pager->display_all = value; pager_update(pager); @@ -521,7 +737,6 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet) { PagerData* pager; GtkActionGroup* action_group; - gboolean display_names; pager = g_new0(PagerData, 1); @@ -535,19 +750,10 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet) pager->n_rows = CLAMP(pager->n_rows, 1, MAX_REASONABLE_ROWS); - display_names = g_settings_get_boolean(pager->settings, "display-workspace-names"); + pager->display_names = g_settings_get_boolean(pager->settings, "display-workspace-names"); pager->wrap_workspaces = g_settings_get_boolean(pager->settings, "wrap-workspaces"); - if (display_names) - { - pager->display_mode = WNCK_PAGER_DISPLAY_NAME; - } - else - { - pager->display_mode = WNCK_PAGER_DISPLAY_CONTENT; - } - pager->display_all = g_settings_get_boolean(pager->settings, "display-all-workspaces"); switch (mate_panel_applet_get_orient(applet)) @@ -563,10 +769,28 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet) break; } - pager->pager = wnck_pager_new(); - pager->screen = NULL; +#ifdef HAVE_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + { + pager->pager = wnck_pager_new(); + wnck_pager_set_shadow_type(WNCK_PAGER(pager->pager), GTK_SHADOW_IN); + } + else +#endif /* HAVE_X11 */ + +#ifdef HAVE_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + { + pager->pager = gtk_label_new ("[Pager not supported on Wayland]"); + } + else +#endif /* HAVE_WAYLAND */ + + { + pager->pager = gtk_label_new ("[Pager not supported on this platform]"); + } + pager->wm = PAGER_WM_UNKNOWN; - wnck_pager_set_shadow_type(WNCK_PAGER(pager->pager), GTK_SHADOW_IN); GtkStyleContext *context; context = gtk_widget_get_style_context (GTK_WIDGET (applet)); @@ -574,23 +798,37 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet) context = gtk_widget_get_style_context (pager->pager); gtk_style_context_add_class (context, "wnck-pager"); - g_signal_connect(G_OBJECT(pager->pager), "destroy", G_CALLBACK(destroy_pager), pager); + g_signal_connect (pager->pager, "destroy", + G_CALLBACK (destroy_pager), + pager); /* overwrite default WnckPager widget scroll-event */ - g_signal_connect(G_OBJECT(pager->pager), "scroll-event", G_CALLBACK(applet_scroll), pager); - - gtk_container_add(GTK_CONTAINER(pager->applet), pager->pager); - - g_signal_connect(G_OBJECT(pager->applet), "realize", G_CALLBACK(applet_realized), pager); - g_signal_connect(G_OBJECT(pager->applet), "unrealize", G_CALLBACK(applet_unrealized), pager); - g_signal_connect(G_OBJECT(pager->applet), "change_orient", G_CALLBACK(applet_change_orient), pager); - g_signal_connect(G_OBJECT(pager->applet), "change_background", G_CALLBACK(applet_change_background), pager); - g_signal_connect(G_OBJECT(pager->applet), "style-updated", G_CALLBACK(applet_style_updated), context); - - gtk_widget_show(pager->pager); - gtk_widget_show(pager->applet); - - mate_panel_applet_set_background_widget(MATE_PANEL_APPLET(pager->applet), GTK_WIDGET(pager->applet)); + g_signal_connect (pager->pager, "scroll-event", + G_CALLBACK (applet_scroll), + pager); + + pager->pager_container = pager_container_new(pager->pager, pager->orientation); + gtk_container_add(GTK_CONTAINER(pager->applet), pager->pager_container); + + g_signal_connect (pager->applet, "realize", + G_CALLBACK (applet_realized), + pager); + g_signal_connect (pager->applet, "unrealize", + G_CALLBACK (applet_unrealized), + pager); + g_signal_connect (pager->applet, "change-orient", + G_CALLBACK (applet_change_orient), + pager); + g_signal_connect (pager->applet, "change-background", + G_CALLBACK (applet_change_background), + pager); + g_signal_connect (pager->applet, "style-updated", + G_CALLBACK (applet_style_updated), + context); + + gtk_widget_show (pager->pager); + gtk_widget_show (pager->pager_container); + gtk_widget_show (pager->applet); action_group = gtk_action_group_new("WorkspaceSwitcher Applet Actions"); gtk_action_group_set_translation_domain(action_group, GETTEXT_PACKAGE); @@ -612,7 +850,6 @@ gboolean workspace_switcher_applet_fill(MatePanelApplet* applet) return TRUE; } - static void display_help_dialog(GtkAction* action, PagerData* pager) { wncklet_display_help(pager->applet, "mate-user-guide", "overview-workspaces", WORKSPACE_SWITCHER_ICON); @@ -641,13 +878,13 @@ static void display_about_dialog(GtkAction* action, PagerData* pager) "comments", _("The Workspace Switcher shows you a small version of your workspaces that lets you manage your windows."), "copyright", _("Copyright \xc2\xa9 2002 Red Hat, Inc.\n" "Copyright \xc2\xa9 2011 Perberos\n" - "Copyright \xc2\xa9 2012-2020 MATE developers"), + "Copyright \xc2\xa9 2012-2021 MATE developers"), "documenters", documenters, "icon-name", WORKSPACE_SWITCHER_ICON, "logo-icon-name", WORKSPACE_SWITCHER_ICON, "translator-credits", _("translator-credits"), "version", VERSION, - "website", "http://www.mate-desktop.org/", + "website", PACKAGE_URL, NULL); } @@ -671,16 +908,34 @@ static void num_rows_value_changed(GtkSpinButton* button, PagerData* pager) g_settings_set_int(pager->settings, "num-rows", gtk_spin_button_get_value_as_int(button)); } +static const char *get_workspace_name(PagerData* pager, int workspace_index) +{ +#ifdef HAVE_X11 + if (pager->screen) + { + WnckWorkspace *workspace = wnck_screen_get_workspace(pager->screen, workspace_index); + return wnck_workspace_get_name(workspace); + } +#endif /* HAVE_X11 */ + + /* Fallback */ + return "workspace"; +} + static void update_workspaces_model(PagerData* pager) { - int nr_ws, i; - WnckWorkspace* workspace; - GtkTreeIter iter; + int nr_ws = 1; - nr_ws = wnck_screen_get_workspace_count(pager->screen); +#ifdef HAVE_X11 + if (pager->screen) + nr_ws = wnck_screen_get_workspace_count (pager->screen); +#endif /* HAVE_X11 */ if (pager->properties_dialog) { + int i; + GtkTreeIter iter; + if (nr_ws != gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pager->num_workspaces_spin))) gtk_spin_button_set_value(GTK_SPIN_BUTTON(pager->num_workspaces_spin), nr_ws); @@ -688,21 +943,21 @@ static void update_workspaces_model(PagerData* pager) for (i = 0; i < nr_ws; i++) { - workspace = wnck_screen_get_workspace(pager->screen, i); gtk_list_store_append(pager->workspaces_store, &iter); - gtk_list_store_set(pager->workspaces_store, &iter, 0, wnck_workspace_get_name(workspace), -1); + const char* name = get_workspace_name(pager, i); + gtk_list_store_set(pager->workspaces_store, &iter, 0, name, -1); } } } +#ifdef HAVE_X11 static void workspace_renamed(WnckWorkspace* space, PagerData* pager) { - int i; GtkTreeIter iter; - i = wnck_workspace_get_number(space); + g_return_if_fail(WNCK_IS_WORKSPACE(space)); - if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pager->workspaces_store), &iter, NULL, i)) + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (pager->workspaces_store), &iter, NULL, wnck_workspace_get_number (space))) gtk_list_store_set(pager->workspaces_store, &iter, 0, wnck_workspace_get_name(space), -1); } @@ -720,10 +975,21 @@ static void workspace_destroyed(WnckScreen* screen, WnckWorkspace* space, PagerD g_return_if_fail(WNCK_IS_SCREEN(screen)); update_workspaces_model(pager); } +#endif /* HAVE_X11 */ -static void num_workspaces_value_changed(GtkSpinButton* button, PagerData* pager) +static void +on_num_workspaces_value_changed (GtkSpinButton *button, + PagerData *pager) { - wnck_screen_change_workspace_count(pager->screen, gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pager->num_workspaces_spin))); +#ifdef HAVE_X11 + if (pager->screen) + { + int workspace_count = gtk_spin_button_get_value_as_int (button); + wnck_screen_change_workspace_count(pager->screen, workspace_count); + if (workspace_count < pager->n_rows) + g_settings_set_int (pager->settings, "num-rows", workspace_count); + } +#endif /* HAVE_X11 */ } static gboolean workspaces_tree_focused_out(GtkTreeView* treeview, GdkEventFocus* event, PagerData* pager) @@ -737,28 +1003,33 @@ static gboolean workspaces_tree_focused_out(GtkTreeView* treeview, GdkEventFocus static void workspace_name_edited(GtkCellRendererText* cell_renderer_text, const gchar* path, const gchar* new_text, PagerData* pager) { - const gint* indices; - WnckWorkspace* workspace; - GtkTreePath* p; +#ifdef HAVE_X11 + if (pager->screen) + { + const gint* indices; + WnckWorkspace* workspace; + GtkTreePath* p; - p = gtk_tree_path_new_from_string(path); - indices = gtk_tree_path_get_indices(p); - workspace = wnck_screen_get_workspace(pager->screen, indices[0]); + p = gtk_tree_path_new_from_string(path); + indices = gtk_tree_path_get_indices(p); + workspace = wnck_screen_get_workspace(pager->screen, indices[0]); - if (workspace != NULL) - { - gchar* temp_name = g_strdup(new_text); + if (workspace != NULL) + { + gchar* temp_name = g_strdup(new_text); - wnck_workspace_change_name(workspace, g_strstrip(temp_name)); + wnck_workspace_change_name(workspace, g_strstrip(temp_name)); - g_free(temp_name); - } - else - { - g_warning("Edited name of workspace %d which no longer exists", indices[0]); - } + g_free(temp_name); + } + else + { + g_warning("Edited name of workspace %d which no longer exists", indices[0]); + } - gtk_tree_path_free(p); + gtk_tree_path_free(p); + } +#endif } static void properties_dialog_destroyed(GtkWidget* widget, PagerData* pager) @@ -855,7 +1126,6 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager) gboolean value; GtkTreeViewColumn* column; GtkCellRenderer* cell; - int nr_ws, i; GSettings *marco_general_settings = NULL; GSettings *marco_workspaces_settings = NULL; @@ -893,7 +1163,6 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager) if (marco_workspaces_settings != NULL) g_object_unref (marco_workspaces_settings); - /* Wrap workspaces: */ if (pager->wrap_workspaces_toggle) { @@ -901,25 +1170,24 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pager->wrap_workspaces_toggle), pager->wrap_workspaces); } - g_signal_connect(G_OBJECT(pager->wrap_workspaces_toggle), "toggled", (GCallback) wrap_workspaces_toggled, pager); + g_signal_connect (pager->wrap_workspaces_toggle, "toggled", + (GCallback) wrap_workspaces_toggled, + pager); /* Display workspace names: */ - g_signal_connect(G_OBJECT(pager->display_workspaces_toggle), "toggled", (GCallback) display_workspace_names_toggled, pager); + g_signal_connect (pager->display_workspaces_toggle, "toggled", + (GCallback) display_workspace_names_toggled, + pager); - if (pager->display_mode == WNCK_PAGER_DISPLAY_NAME) - { - value = TRUE; - } - else - { - value = FALSE; - } + value = pager->display_names; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pager->display_workspaces_toggle), value); /* Display all workspaces: */ - g_signal_connect(G_OBJECT(pager->all_workspaces_radio), "toggled", (GCallback) all_workspaces_toggled, pager); + g_signal_connect (pager->all_workspaces_radio, "toggled", + (GCallback) all_workspaces_toggled, + pager); if (pager->display_all) { @@ -937,25 +1205,41 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager) } /* Num rows: */ - g_signal_connect(G_OBJECT(pager->num_rows_spin), "value_changed", (GCallback) num_rows_value_changed, pager); + g_signal_connect (pager->num_rows_spin, "value-changed", G_CALLBACK (num_rows_value_changed), pager); gtk_spin_button_set_value(GTK_SPIN_BUTTON(pager->num_rows_spin), pager->n_rows); gtk_label_set_text(GTK_LABEL(pager->label_row_col), pager->orientation == GTK_ORIENTATION_HORIZONTAL ? _("rows") : _("columns")); g_signal_connect(pager->properties_dialog, "destroy", G_CALLBACK(properties_dialog_destroyed), pager); - g_signal_connect(pager->properties_dialog, "delete_event", G_CALLBACK(delete_event), pager); + g_signal_connect(pager->properties_dialog, "delete-event", G_CALLBACK(delete_event), pager); g_signal_connect(pager->properties_dialog, "response", G_CALLBACK(response_cb), pager); g_signal_connect(WID("done_button"), "clicked", (GCallback) close_dialog, pager); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(pager->num_workspaces_spin), wnck_screen_get_workspace_count(pager->screen)); - g_signal_connect(G_OBJECT(pager->num_workspaces_spin), "value_changed", (GCallback) num_workspaces_value_changed, pager); +#ifdef HAVE_X11 + if (pager->screen) + { + int i, nr_ws; + gtk_spin_button_set_value(GTK_SPIN_BUTTON(pager->num_workspaces_spin), wnck_screen_get_workspace_count(pager->screen)); + wncklet_connect_while_alive(pager->screen, "workspace_created", G_CALLBACK(workspace_created), pager, pager->properties_dialog); + wncklet_connect_while_alive(pager->screen, "workspace_destroyed", G_CALLBACK(workspace_destroyed), pager, pager->properties_dialog); - wncklet_connect_while_alive(pager->screen, "workspace_created", G_CALLBACK(workspace_created), pager, pager->properties_dialog); + nr_ws = wnck_screen_get_workspace_count(pager->screen); - wncklet_connect_while_alive(pager->screen, "workspace_destroyed", G_CALLBACK(workspace_destroyed), pager, pager->properties_dialog); + for (i = 0; i < nr_ws; i++) + { + wncklet_connect_while_alive(G_OBJECT(wnck_screen_get_workspace(pager->screen, i)), "name_changed", G_CALLBACK(workspace_renamed), pager, pager->properties_dialog); + } + } +#endif /* HAVE_X11 */ - g_signal_connect(G_OBJECT(pager->workspaces_tree), "focus_out_event", (GCallback) workspaces_tree_focused_out, pager); + g_signal_connect (pager->num_workspaces_spin, "value-changed", + G_CALLBACK (on_num_workspaces_value_changed), + pager); + + g_signal_connect (pager->workspaces_tree, "focus-out-event", + (GCallback) workspaces_tree_focused_out, + pager); pager->workspaces_store = gtk_list_store_new(1, G_TYPE_STRING, NULL); update_workspaces_model(pager); @@ -969,13 +1253,6 @@ static void setup_dialog(GtkBuilder* builder, PagerData* pager) gtk_tree_view_append_column(GTK_TREE_VIEW(pager->workspaces_tree), column); g_signal_connect(cell, "edited", (GCallback) workspace_name_edited, pager); - nr_ws = wnck_screen_get_workspace_count(pager->screen); - - for (i = 0; i < nr_ws; i++) - { - wncklet_connect_while_alive(G_OBJECT(wnck_screen_get_workspace(pager->screen, i)), "name_changed", G_CALLBACK(workspace_renamed), pager, pager->properties_dialog); - } - update_properties_for_wm(pager); } |