summaryrefslogtreecommitdiff
path: root/applets/wncklet
diff options
context:
space:
mode:
Diffstat (limited to 'applets/wncklet')
-rw-r--r--applets/wncklet/Makefile.am18
-rw-r--r--applets/wncklet/org.mate.panel.Wncklet.mate-panel-applet.desktop.in.in2
-rw-r--r--applets/wncklet/org.mate.panel.applet.window-list.gschema.xml.in10
-rw-r--r--applets/wncklet/showdesktop.c111
-rw-r--r--applets/wncklet/showdesktop.h1
-rw-r--r--applets/wncklet/wayland-backend.c752
-rw-r--r--applets/wncklet/wayland-backend.h46
-rwxr-xr-xapplets/wncklet/wayland-protocol/generate-code.sh20
-rw-r--r--applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-client.h594
-rw-r--r--applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1-code.c104
-rw-r--r--applets/wncklet/wayland-protocol/wlr-foreign-toplevel-management-unstable-v1.xml259
-rw-r--r--applets/wncklet/window-list.c592
-rw-r--r--applets/wncklet/window-list.ui602
-rw-r--r--applets/wncklet/window-menu.c67
-rw-r--r--applets/wncklet/wncklet.c25
-rw-r--r--applets/wncklet/wncklet.h4
-rw-r--r--applets/wncklet/workspace-switcher.c529
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);
}