2
* Copyright © 2006 Novell, Inc.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
19
* Author: David Reveman <davidr@novell.com>
21
* 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
22
* Frames Management: Copright © 2011 Canonical Ltd.
23
* Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
27
#include "local-menus.h"
38
gint menu_mode = GLOBAL;
40
GDBusProxy *global_lim_listener;
43
#ifdef META_HAS_LOCAL_MENUS
45
gwd_menu_mode_changed (GSettings *settings,
49
menu_mode = g_settings_get_enum (settings, "menu-mode");
53
active_local_menu *active_menu;
54
pending_local_menu *pending_menu;
56
GHashTable *get_windows_with_menus_table ()
58
static GHashTable *windows_with_menus = NULL;
60
if (!windows_with_menus)
61
windows_with_menus = g_hash_table_new (NULL, NULL);
63
return windows_with_menus;
66
static gboolean read_xprop_for_window (Display *dpy, Window xid)
68
Atom ubuntu_appmenu_unique_name = XInternAtom (dpy, "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
69
Atom utf8_string = XInternAtom (dpy, "UTF8_STRING", FALSE);
72
unsigned long nitems, nleft;
74
XGetWindowProperty (dpy, xid, ubuntu_appmenu_unique_name,
75
0L, 16L, FALSE, utf8_string, &actual, &fmt, &nitems, &nleft, &prop);
77
if (actual == utf8_string && fmt == 8 && nitems > 1)
79
g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (ALLOWED));
84
g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (NOT_ALLOWED));
90
local_menu_allowed_on_window (Display *dpy, Window xid)
92
gpointer local_menu_allowed_found = g_hash_table_lookup (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
94
if (local_menu_allowed_found)
96
return GPOINTER_TO_INT (local_menu_allowed_found) == ALLOWED;
100
return read_xprop_for_window (dpy, xid);
107
gwd_window_should_have_local_menu (Window win)
109
#ifdef META_HAS_LOCAL_MENUS
110
const gchar * const *schemas = g_settings_list_schemas ();
111
static GSettings *lim_settings = NULL;
112
while (*schemas != NULL && !lim_settings)
114
if (g_str_equal (*schemas, "com.canonical.indicator.appmenu"))
116
lim_settings = g_settings_new ("com.canonical.indicator.appmenu");
117
menu_mode = g_settings_get_enum (lim_settings, "menu-mode");
118
g_signal_connect (lim_settings, "changed::menu-mode", G_CALLBACK (gwd_menu_mode_changed), NULL);
124
if (lim_settings && win)
125
return menu_mode == LOCAL && local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), win);
132
gwd_get_entry_id_from_sync_variant (GVariant *args)
134
/* We need to get the indicator data once we've called show */
136
GVariantIter* iter = NULL;
137
gchar* name_hint = NULL;
138
gchar* indicator_id = NULL;
139
gchar* entry_id = NULL;
141
gboolean label_sensitive = FALSE;
142
gboolean label_visible = FALSE;
143
guint32 image_type = 0;
144
gchar* image_data = NULL;
145
gboolean image_sensitive = FALSE;
146
gboolean image_visible = FALSE;
147
gint32 priority = -1;
149
g_variant_get (args, "(a(ssssbbusbbi))", &iter);
150
while (g_variant_iter_loop (iter, "(ssssbbusbbi)",
163
g_variant_unref (args);
164
return g_strdup (entry_id);
167
g_variant_unref (args);
168
g_assert_not_reached ();
171
#ifdef META_HAS_LOCAL_MENUS
173
on_local_menu_activated (GDBusProxy *proxy,
176
GVariant *parameters,
180
if (g_strcmp0 (signal_name, "EntryActivated") == 0)
182
show_local_menu_data *d = (show_local_menu_data *) user_data;
183
gchar *entry_id = NULL;
187
g_variant_get (parameters, "(s(iiuu))", &entry_id, &x_out, &y_out, &width, &height, NULL);
189
if (!d->local_menu_entry_id)
191
GError *error = NULL;
192
GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
193
GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
195
g_assert_no_error (error);
196
d->local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
199
if (g_strcmp0 (entry_id, d->local_menu_entry_id) == 0)
201
d->rect->x = x_out - d->dx;
202
d->rect->y = y_out - d->dy;
203
d->rect->width = width;
204
d->rect->height = height;
205
(*d->cb) (d->user_data);
207
else if (g_strcmp0 (entry_id, "") == 0)
209
memset (d->rect, 0, sizeof (GdkRectangle));
210
(*d->cb) (d->user_data);
212
g_signal_handlers_disconnect_by_func (proxy, on_local_menu_activated, d);
216
g_free (active_menu);
220
g_free (d->local_menu_entry_id);
228
gwd_move_window_instead (gpointer user_data)
230
(*pending_menu->cb) (pending_menu->user_data);
231
g_source_remove (pending_menu->move_timeout_id);
232
g_free (pending_menu->user_data);
233
g_free (pending_menu);
239
local_menu_process_motion(gint x_root, gint y_root)
244
if (abs (pending_menu->x_root - x_root) > 4 &&
245
abs (pending_menu->y_root - y_root) > 4)
246
gwd_move_window_instead (pending_menu);
250
gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
251
gpointer user_data_start_move_window,
257
g_source_remove (pending_menu->move_timeout_id);
258
g_free (pending_menu->user_data);
259
g_free (pending_menu);
263
pending_menu = g_new0 (pending_local_menu, 1);
264
pending_menu->cb = start_move_window;
265
pending_menu->user_data = user_data_start_move_window;
266
pending_menu->move_timeout_id = g_timeout_add (150, gwd_move_window_instead, pending_menu);
269
#ifdef META_HAS_LOCAL_MENUS
271
local_menu_entry_activated_request (GDBusProxy *proxy,
274
GVariant *parameters,
277
if (g_strcmp0 (signal_name, "EntryActivateRequest") == 0)
279
gchar *activated_entry_id;
280
gchar *local_menu_entry_id;
281
local_menu_entry_activated_request_funcs *funcs = (local_menu_entry_activated_request_funcs *) user_data;
283
g_variant_get (parameters, "(s)", &activated_entry_id, NULL);
285
GError *error = NULL;
286
GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
287
GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
289
g_assert_no_error (error);
290
local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
292
if (g_strcmp0 (activated_entry_id, local_menu_entry_id) == 0)
294
WnckScreen *screen = wnck_screen_get_for_root (gdk_x11_get_default_root_xwindow ());
295
int dx, dy, top_height;
300
Box *rect = (*funcs->active_window_local_menu_rect_callback) ((gpointer) screen, &dx, &dy, &top_height, &xid);
307
gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
308
xid, x + dx, y + dy, x, y, 0, 0,
309
funcs->show_window_menu_hidden_callback, GINT_TO_POINTER (xid));
318
gwd_show_local_menu (Display *xdisplay,
319
Window frame_xwindow,
326
show_window_menu_hidden_cb cb,
327
gpointer user_data_show_window_menu)
331
g_source_remove (pending_menu->move_timeout_id);
332
g_free (pending_menu->user_data);
333
g_free (pending_menu);
337
#ifdef META_HAS_LOCAL_MENUS
340
XUngrabPointer (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
341
XUngrabKeyboard (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
342
XSync (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), FALSE);
344
GDBusProxy *proxy = global_lim_listener;
348
GVariant *message = g_variant_new ("(uiiu)", frame_xwindow, x, y, time);
349
GError *error = NULL;
350
g_dbus_proxy_call_sync (proxy, "ShowAppMenu", message, 0, 500, NULL, &error);
353
g_print ("error calling ShowAppMenu: %s\n", error->message);
357
show_local_menu_data *data = g_new0 (show_local_menu_data, 1);
360
g_free (active_menu);
362
active_menu = g_new0 (active_local_menu, 1);
365
data->user_data = user_data_show_window_menu;
366
data->rect = &active_menu->rect;
367
data->dx = x - x_win;
368
data->dy = y - y_win;
369
data->local_menu_entry_id = NULL;
371
g_signal_connect (proxy, "g-signal", G_CALLBACK (on_local_menu_activated), data);
380
force_local_menus_on (Window win,
381
MetaButtonLayout *button_layout)
383
#ifdef META_HAS_LOCAL_MENUS
384
if (gwd_window_should_have_local_menu (win))
386
if (button_layout->left_buttons[0] != META_BUTTON_FUNCTION_LAST)
389
for (; i < MAX_BUTTONS_PER_CORNER; i++)
391
if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
393
else if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_LAST)
395
if ((i + 1) < MAX_BUTTONS_PER_CORNER)
397
button_layout->left_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
398
button_layout->left_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
404
if (button_layout->right_buttons[0] != META_BUTTON_FUNCTION_LAST)
407
for (; i < MAX_BUTTONS_PER_CORNER; i++)
409
if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
411
else if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_LAST)
413
if ((i + 1) < MAX_BUTTONS_PER_CORNER)
415
button_layout->right_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
416
button_layout->right_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
427
local_menu_cache_notify_window_destroyed (Window xid)
429
g_hash_table_remove (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
433
local_menu_cache_reload_xwindow (Display *dpy, Window xid)
435
read_xprop_for_window (dpy, xid);