Merge lp:~smspillaz/compiz-core/compiz-core.lim into lp:compiz-core

Proposed by Sam Spilsbury
Status: Superseded
Proposed branch: lp:~smspillaz/compiz-core/compiz-core.lim
Merge into: lp:compiz-core
Diff against target: 1432 lines (+1077/-11)
16 files modified
gtk/window-decorator/CMakeLists.txt (+6/-0)
gtk/window-decorator/decorator.c (+32/-6)
gtk/window-decorator/events.c (+115/-3)
gtk/window-decorator/gtk-window-decorator.c (+56/-0)
gtk/window-decorator/gtk-window-decorator.h (+8/-1)
gtk/window-decorator/local-menus/CMakeLists.txt (+60/-0)
gtk/window-decorator/local-menus/src/local-menus.c (+423/-0)
gtk/window-decorator/local-menus/src/local-menus.h (+120/-0)
gtk/window-decorator/local-menus/tests/CMakeLists.txt (+3/-0)
gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt (+21/-0)
gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp (+27/-0)
gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt (+21/-0)
gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp (+77/-0)
gtk/window-decorator/local-menus/tests/test-local-menu.h (+49/-0)
gtk/window-decorator/metacity.c (+54/-0)
gtk/window-decorator/wnck.c (+5/-1)
To merge this branch: bzr merge lp:~smspillaz/compiz-core/compiz-core.lim
Reviewer Review Type Date Requested Status
Marco Trevisan (Treviño) Needs Fixing
Daniel van Vugt Approve
Alan Griffiths Abstain
Sam Spilsbury Pending
Review via email: mp+94641@code.launchpad.net

This proposal supersedes a proposal from 2012-02-22.

This proposal has been superseded by a proposal from 2012-02-28.

Description of the change

Adds support for Ubuntu's "locally integrated menu bars" into gtk-window-decorator.

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

8 +#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)

No plausible reason for using a macro. In C++ constants (enum or unsigned int) are better as they respect scope rules. Also, should this be inside "#ifdef META_HAS_LOCAL_MENUS" conditional?

296 button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
297
298 - for (i = 3; i < MAX_BUTTONS_PER_CORNER; i++)
299 + for (i = 4; i < MAX_BUTTONS_PER_CORNER; i++)
300 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
301 }

I'm not sure what the magic number "3"->"4" is. But we now seem to miss 3 entirely. Is that correct?

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

> 8 +#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)
>
> No plausible reason for using a macro. In C++ constants (enum or unsigned
> int) are better as they respect scope rules. Also, should this be inside
> "#ifdef META_HAS_LOCAL_MENUS" conditional?
>

Its C

>
> 296 button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
> 297
> 298 - for (i = 3; i < MAX_BUTTONS_PER_CORNER; i++)
> 299 + for (i = 4; i < MAX_BUTTONS_PER_CORNER; i++)
> 300 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
> 301 }
>
> I'm not sure what the magic number "3"->"4" is. But we now seem to miss 3
> entirely. Is that correct?

Its for initializing the array, though maybe thats wrong. Good find let me check

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Please resubmit for target branch lp:compiz-core (0.9.7)

review: Needs Resubmitting
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Looks better

review: Approve
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

(More work needed to adopt to trevino;s changes)

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Fails to build ifndef META_HAS_LOCAL_MENUS

[ 0%] Building C object gtk/window-decorator/local-menus/CMakeFiles/gtk_window_decorator_local_menus.dir/src/local-menus.c.o
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/src/local-menus.c:36:1: error: ‘gwd_menu_mode_changed’ defined but not used [-Werror=unused-function]
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/src/local-menus.c:72:1: error: ‘on_local_menu_activated’ defined but not used [-Werror=unused-function]
cc1: all warnings being treated as errors

review: Needs Fixing
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

+ gboolean empty = g_strcmp0 (entry_id, "") == 0;

You can do the same with:
gboolean empty = (!entry_id || entry_id[0] == '\0');

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

If we're trying to be cryptic...

   gboolean empty = g_strcmp0 (entry_id, "") == 0;

can be written:

   gboolean empty = !entry_id || !*entry_id;

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, Sam remember to update the "EntryActivated" signal against the new signature s(iiuu).

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Yep, done

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Fails to build for a whole new reason now :(

Scanning dependencies of target gtk_window_decorator_force_local_menu_on_test
[ 5%] Building CXX object gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/test-force-local-menu-on.cpp.o
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp: In member function ‘virtual void GtkWindowDecoratorTestLocalMenuLayout_TestForceNoCloseButtonSet_Test::TestBody()’:
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp:56:5: error: ‘META_BUTTON_FUNCTION_WINDOW_MENU’ was not declared in this scope
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp: In member function ‘virtual void GtkWindowDecoratorTestLocalMenuLayout_TestForceNoCloseMinimizeMaximizeButtonSet_Test::TestBody()’:
/home/dan/bzr/compiz-core/lim/gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp:69:5: error: ‘META_BUTTON_FUNCTION_WINDOW_MENU’ was not declared in this scope
make[2]: *** [gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/test-force-local-menu-on.cpp.o] Error 1
make[1]: *** [gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeFiles/gtk_window_decorator_force_local_menu_on_test.dir/all] Error 2
make: *** [all] Error 2

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Tests needed to be disabled on META_HAS_LOCAL_MENUS, thanks for catching that.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Test cases fail (crash). Maybe they need to be disabled if local menus are disabled...

The following tests FAILED:
   1 - gtk_window_decorator_local_menus_on_off (SEGFAULT)
   2 - gtk_window_decorator_force_local_menu_on (SEGFAULT)
Errors while running CTest
make: *** [test] Error 8

-----

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
(gdb) bt
#0 0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#1 0x00007ffff709e2f5 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#2 0x00007ffff6dc29f0 in g_object_unref ()
   from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#3 0x000000000042ef7d in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#4 0x000000000042751f in testing::Test::Run() ()
#5 0x000000000042763d in testing::TestInfo::Run() ()
#6 0x0000000000427787 in testing::TestCase::Run() ()
#7 0x0000000000427a17 in testing::internal::UnitTestImpl::RunAllTests() ()
#8 0x000000000042eafd in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ()
#9 0x0000000000426c0b in testing::UnitTest::Run() ()
#10 0x0000000000413145 in main ()

-----

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
(gdb) bt
#0 0x00007ffff709bb70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#1 0x00007ffff709e2f5 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
#2 0x00007ffff6dc29f0 in g_object_unref ()
   from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
#3 0x000000000042ef7d in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) ()
#4 0x000000000042751f in testing::Test::Run() ()
#5 0x000000000042763d in testing::TestInfo::Run() ()
#6 0x0000000000427787 in testing::TestCase::Run() ()
#7 0x0000000000427a17 in testing::internal::UnitTestImpl::RunAllTests() ()
#8 0x000000000042eafd in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) ()
#9 0x0000000000426c0b in testing::UnitTest::Run() ()
#10 0x0000000000413145 in main ()

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Huh, that's weird. I've disabled META_HAS_LOCAL_MENUS here, and make sure to link to the system libmetacity-private and the tests are still passing for me.

Can you post the output of the individual tests before it reaches the point where they crash? Or do they just crash without outputting anything ?

(Could be something to do with the SetUp () function, so I'll give that a try

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Confirmed all unit tests now pass and no obvious bugs in manual testing.

I still think we need to be tidier with the CMake syntax as I've mentioned before...

review: Approve
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Yes, one day I'm going to introduce a bunch of functions to the CMake which reduce the verbosity.

Daniel - if you want to test this for realz, you can use the following:

lp:~smspillaz/metacity/metacity.lim
lp:~3v1n0/unity/lim-panel
lp:~indicator-applet-developers/indicator-appmenu/local-menus-single-menu
lp:~smspillaz/light-themes/light-themes.limv4

Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

Looks OK, Builds OK, automated tests run. (With no META_HAS_LOCAL_MENUS)

review: Approve
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

I've tested it, to be even better, maybe you could also sync the title geometries once you opened/closed the menu, but that can be optional.
One thing you need to fix is the double-click over the title, that currently doesn't work as expected.
Look at the logic I've used in PanelTitlebarGrabArea.

Then I guess we need to define with design better one case: if a window is unfocused, and the user presses over the title / icon, should we just focus it or also open the menu? Currently we do both, but I'm not sure this is the best choice.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, don't care about syncing geometries. You don't need that and could cause problems to the panel... :)

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, one more thing. Please check if the window you're decorating has the "_UBUNTU_APPMENU_UNIQUE_NAME" utf8 atom set to check if it has menus.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote : Posted in a previous version of this proposal

Ah, I forgot... The decorator is still missing to react to the com.canonical.Unity.Panel.Service.EntryActivateRequest signal, that happens when you do Alt+F on terminal for example... :)

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

OK, I'll take care of these two things too. Thanks.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

No obvious errors

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Confirmed again all unit tests now pass and no obvious bugs in manual testing.

Though, like Alan, I have not reviewed the theoretical correctness of the code in detail.

review: Approve
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

Mostly it works, well.

By the way, I've tested the support for the EntryActivateRequest and even if it's correctly handled if a window is in focus (i.e. pressing Alt+F on the terminal causes the menu to show in the right place), otherwise if you have this scenario: a maximized window in focus, and a restored window unfocused, when pressing Alt+F (or something, that will be managed by the focused window) gtk-window-decorator crashes.

This is the stacktrace I have: http://paste.ubuntu.com/859831/
Actually you should just ignore that signal if no decorated window is in focus.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Ack.

3002. By Sam Spilsbury

Fix a crash when trying to show the window menu on a non decorated window

3003. By Sam Spilsbury

Tell us if we have them

3004. By Sam Spilsbury

Merged lp:compiz-core

3005. By Sam Spilsbury

We also need to test the property too

3006. By Sam Spilsbury

Grab buttons too so that we can process motion events on the titlebar and
move the window instead.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'gtk/window-decorator/CMakeLists.txt'
2--- gtk/window-decorator/CMakeLists.txt 2011-02-16 17:39:56 +0000
3+++ gtk/window-decorator/CMakeLists.txt 2012-02-25 05:11:17 +0000
4@@ -4,7 +4,10 @@
5 set (CMAKE_INSTALL_RPATH ${libdir})
6 endif (COMPIZ_BUILD_WITH_RPATH)
7
8+ add_subdirectory (local-menus)
9+
10 include_directories (
11+ ${CMAKE_CURRENT_SOURCE_DIR}/local-menus/src
12 ${compiz_SOURCE_DIR}/include
13 ${CMAKE_BINARY_DIR}/gtk
14 ${GTK_WINDOW_DECORATOR_INCLUDE_DIRS}
15@@ -63,6 +66,9 @@
16 target_link_libraries (
17 gtk-window-decorator
18 decoration
19+
20+ gtk_window_decorator_local_menus
21+
22 ${GTK_WINDOW_DECORATOR_LIBRARIES}
23 ${GCONF_LIBRARIES}
24 ${DBUS_GLIB_LIBRARIES}
25
26=== modified file 'gtk/window-decorator/decorator.c'
27--- gtk/window-decorator/decorator.c 2012-02-20 07:42:20 +0000
28+++ gtk/window-decorator/decorator.c 2012-02-25 05:11:17 +0000
29@@ -24,6 +24,7 @@
30 */
31
32 #include "gtk-window-decorator.h"
33+#include "local-menus.h"
34
35 decor_frame_t *
36 create_normal_frame (const gchar *type)
37@@ -230,12 +231,16 @@
38 void
39 update_event_windows (WnckWindow *win)
40 {
41+#define GWD_SHOW_LOCAL_MENU (WNCK_WINDOW_ACTION_BELOW << 1)
42 Display *xdisplay;
43 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
44 gint x0, y0, width, height, x, y, w, h;
45 gint i, j, k, l;
46 gint actions = d->actions;
47
48+ if (gwd_window_should_have_local_menu (win))
49+ actions |= GWD_SHOW_LOCAL_MENU;
50+
51 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
52
53 /* Get the geometry of the client */
54@@ -298,11 +303,19 @@
55 XMapWindow (xdisplay, d->event_windows[i][j].window);
56 XMoveResizeWindow (xdisplay, d->event_windows[i][j].window,
57 x, y, w, h);
58+
59+ BoxPtr box = &d->event_windows[i][j].pos;
60+ box->x1 = x;
61+ box->x2 = x + w;
62+ box->y1 = y;
63+ box->y2 = y + h;
64 }
65 /* No parent and no geometry - unmap all event windows */
66 else if (!d->frame_window)
67 {
68 XUnmapWindow (xdisplay, d->event_windows[i][j].window);
69+
70+ memset (&d->event_windows[i][j].pos, 0, sizeof (Box));
71 }
72 }
73 }
74@@ -326,12 +339,18 @@
75 WNCK_WINDOW_ACTION_STICK,
76 WNCK_WINDOW_ACTION_UNSHADE,
77 WNCK_WINDOW_ACTION_ABOVE,
78- WNCK_WINDOW_ACTION_UNSTICK
79-#else
80- 0,
81- 0,
82- 0,
83- 0,
84+ WNCK_WINDOW_ACTION_UNSTICK,
85+#else
86+ 0,
87+ 0,
88+ 0,
89+ 0,
90+ 0,
91+#endif
92+
93+#ifdef META_HAS_LOCAL_MENUS
94+ GWD_SHOW_LOCAL_MENU
95+#else
96 0
97 #endif
98
99@@ -371,10 +390,17 @@
100 Window win = d->button_windows[i].window;
101 XMapWindow (xdisplay, win);
102 XMoveResizeWindow (xdisplay, win, x, y, w, h);
103+
104+ BoxPtr box = &d->button_windows[i].pos;
105+ box->x1 = x;
106+ box->x2 = x + w;
107+ box->y1 = y;
108+ box->y2 = y + h;
109 }
110 else if (!d->frame_window)
111 {
112 XUnmapWindow (xdisplay, d->button_windows[i].window);
113+ memset (&d->button_windows[i].pos, 0, sizeof (Box));
114 }
115 }
116
117
118=== modified file 'gtk/window-decorator/events.c'
119--- gtk/window-decorator/events.c 2012-02-20 07:46:35 +0000
120+++ gtk/window-decorator/events.c 2012-02-25 05:11:17 +0000
121@@ -24,6 +24,16 @@
122 */
123
124 #include "gtk-window-decorator.h"
125+#include "local-menus.h"
126+
127+typedef struct _delayed_move_info
128+{
129+ WnckWindow *win;
130+ int x_root;
131+ int y_root;
132+ unsigned int button;
133+ unsigned int time;
134+} delayed_move_info;
135
136 void
137 move_resize_window (WnckWindow *win,
138@@ -65,8 +75,9 @@
139 ev.xclient.data.l[3] = gtkwd_event->button;
140 ev.xclient.data.l[4] = 1;
141
142- XUngrabPointer (xdisplay, gtkwd_event->time);
143- XUngrabKeyboard (xdisplay, gtkwd_event->time);
144+ XAllowEvents (xdisplay, AsyncKeyboard | AsyncPointer, CurrentTime);
145+ XUngrabPointer (xdisplay, CurrentTime);
146+ XUngrabKeyboard (xdisplay, CurrentTime);
147
148 XSendEvent (xdisplay, xroot, FALSE,
149 SubstructureRedirectMask | SubstructureNotifyMask,
150@@ -75,6 +86,24 @@
151 XSync (xdisplay, FALSE);
152 }
153
154+static gboolean
155+move_resize_window_on_timeout (gpointer user_data)
156+{
157+ delayed_move_info *info = (delayed_move_info *) user_data;
158+
159+ decor_event event;
160+ event.x = 0;
161+ event.y = 0;
162+ event.x_root = info->x_root;
163+ event.y_root = info->y_root;
164+ event.button = info->button;
165+ event.window = wnck_window_get_xid (info->win);
166+
167+ move_resize_window (info->win, WM_MOVERESIZE_MOVE, &event);
168+
169+ return FALSE;
170+}
171+
172 void
173 common_button_event (WnckWindow *win,
174 decor_event *gtkwd_event,
175@@ -380,6 +409,75 @@
176 }
177
178 void
179+on_local_menu_hidden (gpointer user_data)
180+{
181+ Window xid = GPOINTER_TO_INT (user_data);
182+ WnckWindow *win = wnck_window_get (xid);
183+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
184+
185+ d->button_states[BUTTON_WINDOW_MENU] &= ~PRESSED_EVENT_WINDOW;
186+
187+ queue_decor_draw (d);
188+}
189+
190+void
191+window_menu_button_event (WnckWindow *win,
192+ decor_event *gtkwd_event,
193+ decor_event_type gtkwd_type)
194+{
195+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
196+ guint state = d->button_states[BUTTON_WINDOW_MENU];
197+
198+ common_button_event (win, gtkwd_event, gtkwd_type,
199+ BUTTON_WINDOW_MENU, 1, _("Window Menu"));
200+
201+ switch (gtkwd_type) {
202+ case GButtonPress:
203+ if (gtkwd_event->button == 1)
204+ {
205+ if (d->button_states[BUTTON_WINDOW_MENU] & BUTTON_EVENT_ACTION_STATE)
206+ {
207+ delayed_move_info *info = g_new0 (delayed_move_info, 1);
208+
209+ info->button = gtkwd_event->button;
210+ info->time = gtkwd_event->time;
211+ info->win = win;
212+ info->x_root = gtkwd_event->x_root;
213+ info->y_root = gtkwd_event->y_root;
214+
215+ gwd_prepare_show_local_menu ((start_move_window_cb) move_resize_window_on_timeout, (gpointer) info);
216+ }
217+ }
218+ break;
219+ case GButtonRelease:
220+ if (gtkwd_event->button == 1)
221+ if (state)
222+ {
223+ int win_x, win_y;
224+ int box_x = d->button_windows[BUTTON_WINDOW_MENU].pos.x1;
225+
226+ wnck_window_get_geometry (win, &win_x, &win_y, NULL, NULL);
227+
228+ int x = win_x + box_x;
229+ int y = win_y + d->context->extents.top;
230+
231+ gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
232+ wnck_window_get_xid (win),
233+ x, y,
234+ box_x,
235+ d->context->extents.top,
236+ gtkwd_event->button,
237+ gtkwd_event->time,
238+ (show_window_menu_hidden_cb) on_local_menu_hidden,
239+ GINT_TO_POINTER (wnck_window_get_xid (d->win)));
240+ }
241+ break;
242+ default:
243+ break;
244+ }
245+}
246+
247+void
248 handle_title_button_event (WnckWindow *win,
249 int action,
250 decor_event *gtkwd_event)
251@@ -485,7 +583,17 @@
252
253 restack_window (win, Above);
254
255- move_resize_window (win, WM_MOVERESIZE_MOVE, gtkwd_event);
256+ delayed_move_info *info = g_new0 (delayed_move_info, 1);
257+
258+ info->x_root = gtkwd_event->x_root;
259+ info->y_root = gtkwd_event->y_root;
260+ info->button = gtkwd_event->button;
261+ info->time = gtkwd_event->time;
262+ info->win = win;
263+
264+ move_resize_window_on_timeout ((gpointer) info);
265+
266+ g_free (info);
267 }
268 }
269 else if (gtkwd_event->button == 2)
270@@ -1031,6 +1139,10 @@
271 if (get_window_prop (xevent->xproperty.window, select_window_atom, &select))
272 update_switcher_window (xevent->xproperty.window, select);
273 }
274+ else if (xevent->xproperty.atom == ubuntu_appmenu_unique_name_atom)
275+ {
276+ local_menu_cache_reload_xwindow (gdk_x11_display_get_xdisplay (gdkdisplay), xevent->xproperty.window);
277+ }
278 break;
279 case DestroyNotify:
280 g_hash_table_remove (frame_table,
281
282=== modified file 'gtk/window-decorator/gtk-window-decorator.c'
283--- gtk/window-decorator/gtk-window-decorator.c 2011-10-13 12:22:14 +0000
284+++ gtk/window-decorator/gtk-window-decorator.c 2012-02-25 05:11:17 +0000
285@@ -24,6 +24,7 @@
286 */
287
288 #include "gtk-window-decorator.h"
289+#include "local-menus.h"
290
291 gboolean minimal = FALSE;
292
293@@ -48,6 +49,7 @@
294 Atom toolkit_action_atom;
295 Atom toolkit_action_window_menu_atom;
296 Atom toolkit_action_force_quit_dialog_atom;
297+Atom ubuntu_appmenu_unique_name_atom;
298
299 Atom net_wm_state_atom;
300 Atom net_wm_state_modal_atom;
301@@ -128,6 +130,35 @@
302 "normal", "modal_dialog", "dialog", "menu", "utility"
303 };
304
305+Box *
306+get_active_window_local_menu_rectangle (gpointer user_data, int *dx, int *dy, int *top_height, Window *xid)
307+{
308+ WnckScreen *screen = (WnckScreen *) user_data;
309+ WnckWindow *window = wnck_screen_get_active_window (screen);
310+ int width, height;
311+ decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
312+
313+ wnck_window_get_geometry (window, dx, dy, &width, &height);
314+
315+ *top_height = d->context->extents.top;
316+
317+ *xid = wnck_window_get_xid (window);
318+
319+ Box *rect = &d->button_windows[BUTTON_WINDOW_MENU].pos;
320+
321+ return rect;
322+}
323+
324+void
325+on_local_menu_window_menu_updated (gpointer user_data)
326+{
327+ Window xid = GPOINTER_TO_INT (user_data);
328+ WnckWindow *window = wnck_window_get (xid);
329+ decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
330+
331+ queue_decor_draw (d);
332+}
333+
334 int
335 main (int argc, char *argv[])
336 {
337@@ -304,6 +335,7 @@
338
339 net_wm_state_atom = XInternAtom (xdisplay,"_NET_WM_STATE", 0);
340 net_wm_state_modal_atom = XInternAtom (xdisplay, "_NET_WM_STATE_MODAL", 0);
341+ ubuntu_appmenu_unique_name_atom = XInternAtom (xdisplay, "_UBUNTU_APPMENU_UNIQUE_NAME", 0);
342
343 status = decor_acquire_dm_session (xdisplay,
344 gdk_screen_get_number (gdkscreen),
345@@ -448,8 +480,32 @@
346
347 update_default_decorations (gdkscreen);
348
349+#ifdef META_HAS_LOCAL_MENUS
350+ GDBusConnection *conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
351+ local_menu_entry_activated_request_funcs funcs =
352+ {
353+ get_active_window_local_menu_rectangle,
354+ on_local_menu_window_menu_updated
355+ };
356+
357+ if (conn)
358+ {
359+ global_lim_listener = g_dbus_proxy_new_sync (conn, 0, NULL, "com.canonical.Unity.Panel.Service",
360+ "/com/canonical/Unity/Panel/Service",
361+ "com.canonical.Unity.Panel.Service",
362+ NULL, NULL);
363+
364+ g_signal_connect (G_OBJECT (global_lim_listener), "g-signal", G_CALLBACK (local_menu_entry_activated_request), (gpointer) &funcs);
365+ }
366+#endif
367 gtk_main ();
368+#ifdef META_HAS_LOCAL_MENUS
369+ if (global_lim_listener)
370+ g_object_unref (global_lim_listener);
371
372+ if (conn)
373+ g_object_unref (conn);
374+#endif
375 win = windows = wnck_screen_get_windows (screen);
376
377 while (win != NULL)
378
379=== modified file 'gtk/window-decorator/gtk-window-decorator.h'
380--- gtk/window-decorator/gtk-window-decorator.h 2011-10-13 12:22:14 +0000
381+++ gtk/window-decorator/gtk-window-decorator.h 2012-02-25 05:11:17 +0000
382@@ -306,6 +306,7 @@
383 extern Atom toolkit_action_force_quit_dialog_atom;
384 extern Atom net_wm_state_atom;
385 extern Atom net_wm_state_modal_atom;
386+extern Atom ubuntu_appmenu_unique_name_atom;
387
388 extern Time dm_sn_timestamp;
389
390@@ -328,7 +329,8 @@
391 #define BUTTON_UNSHADE 7
392 #define BUTTON_UNABOVE 8
393 #define BUTTON_UNSTICK 9
394-#define BUTTON_NUM 10
395+#define BUTTON_WINDOW_MENU 10
396+#define BUTTON_NUM 11
397
398 struct _pos {
399 int x, y, w, h;
400@@ -1013,6 +1015,11 @@
401 decor_event_type gtkwd_type);
402
403 void
404+window_menu_button_event (WnckWindow *win,
405+ decor_event *gtkwd_event,
406+ decor_event_type gtkwd_type);
407+
408+void
409 handle_title_button_event (WnckWindow *win,
410 int action,
411 decor_event *gtkwd_event);
412
413=== added directory 'gtk/window-decorator/local-menus'
414=== added file 'gtk/window-decorator/local-menus/CMakeLists.txt'
415--- gtk/window-decorator/local-menus/CMakeLists.txt 1970-01-01 00:00:00 +0000
416+++ gtk/window-decorator/local-menus/CMakeLists.txt 2012-02-25 05:11:17 +0000
417@@ -0,0 +1,60 @@
418+pkg_check_modules(
419+ LOCAL_MENUS
420+ REQUIRED
421+ glib-2.0 gio-2.0 libwnck-1.0 gtk+-2.0>=2.18.0
422+)
423+
424+INCLUDE_DIRECTORIES(
425+ ${CMAKE_CURRENT_SOURCE_DIR}/include
426+ ${CMAKE_CURRENT_SOURCE_DIR}/src
427+
428+ ${compiz_SOURCE_DIR}/gtk/window-decorator
429+
430+ ${Boost_INCLUDE_DIRS}
431+
432+ ${LOCAL_MENUS_INCLUDE_DIRS}
433+ ${METACITY_INCLUDE_DIRS}
434+)
435+
436+LINK_DIRECTORIES (${LOCAL_MENUS_LIBRARY_DIRS})
437+
438+SET(
439+ PUBLIC_HEADERS
440+)
441+
442+SET(
443+ PRIVATE_HEADERS
444+ ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.h
445+)
446+
447+SET(
448+ SRCS
449+ ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.c
450+)
451+
452+ADD_LIBRARY(
453+ gtk_window_decorator_local_menus STATIC
454+
455+ ${SRCS}
456+
457+ ${PUBLIC_HEADERS}
458+ ${PRIVATE_HEADERS}
459+)
460+
461+IF (COMPIZ_BUILD_TESTING)
462+ADD_SUBDIRECTORY( ${CMAKE_CURRENT_SOURCE_DIR}/tests )
463+ENDIF (COMPIZ_BUILD_TESTING)
464+
465+SET_TARGET_PROPERTIES(
466+ gtk_window_decorator_local_menus PROPERTIES
467+ PUBLIC_HEADER "${PUBLIC_HEADERS}"
468+)
469+
470+install (FILES ${PUBLIC_HEADERS} DESTINATION ${COMPIZ_CORE_INCLUDE_DIR})
471+
472+TARGET_LINK_LIBRARIES(
473+ gtk_window_decorator_local_menus
474+
475+ ${LOCAL_MENUS_LIBRARIES}
476+)
477+
478
479=== added directory 'gtk/window-decorator/local-menus/include'
480=== added directory 'gtk/window-decorator/local-menus/src'
481=== added file 'gtk/window-decorator/local-menus/src/local-menus.c'
482--- gtk/window-decorator/local-menus/src/local-menus.c 1970-01-01 00:00:00 +0000
483+++ gtk/window-decorator/local-menus/src/local-menus.c 2012-02-25 05:11:17 +0000
484@@ -0,0 +1,423 @@
485+/*
486+ * Copyright © 2006 Novell, Inc.
487+ *
488+ * This library is free software; you can redistribute it and/or
489+ * modify it under the terms of the GNU Lesser General Public
490+ * License as published by the Free Software Foundation; either
491+ * version 2 of the License, or (at your option) any later version.
492+ *
493+ * This library is distributed in the hope that it will be useful,
494+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
495+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
496+ * Lesser General Public License for more details.
497+ *
498+ * You should have received a copy of the GNU Lesser General Public
499+ * License along with this library; if not, write to the
500+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
501+ * Boston, MA 02111-1307, USA.
502+ *
503+ * Author: David Reveman <davidr@novell.com>
504+ *
505+ * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
506+ * Frames Management: Copright © 2011 Canonical Ltd.
507+ * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
508+ */
509+
510+#include <string.h>
511+#include "local-menus.h"
512+#include <gdk/gdk.h>
513+#include <gdk/gdkx.h>
514+
515+#define GLOBAL 0
516+#define LOCAL 1
517+
518+#define ALLOWED 2
519+#define NOT_ALLOWED 1
520+
521+gint menu_mode = GLOBAL;
522+
523+GDBusProxy *global_lim_listener;
524+
525+
526+#ifdef META_HAS_LOCAL_MENUS
527+static void
528+gwd_menu_mode_changed (GSettings *settings,
529+ gchar *key,
530+ gpointer user_data)
531+{
532+ menu_mode = g_settings_get_enum (settings, "menu-mode");
533+}
534+#endif
535+
536+active_local_menu *active_menu;
537+pending_local_menu *pending_menu;
538+
539+GHashTable *get_windows_with_menus_table ()
540+{
541+ static GHashTable *windows_with_menus = NULL;
542+
543+ if (!windows_with_menus)
544+ windows_with_menus = g_hash_table_new (NULL, NULL);
545+
546+ return windows_with_menus;
547+}
548+
549+static gboolean read_xprop_for_window (Display *dpy, Window xid)
550+{
551+ Atom ubuntu_appmenu_unique_name = XInternAtom (dpy, "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
552+ Atom utf8_string = XInternAtom (dpy, "UTF8_STRING", FALSE);
553+ Atom actual;
554+ int fmt;
555+ unsigned long nitems, nleft;
556+ unsigned char *prop;
557+ XGetWindowProperty (dpy, xid, ubuntu_appmenu_unique_name,
558+ 0L, 16L, FALSE, utf8_string, &actual, &fmt, &nitems, &nleft, &prop);
559+
560+ if (actual == utf8_string && fmt == 8 && nitems > 1)
561+ {
562+ g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (ALLOWED));
563+ return TRUE;
564+ }
565+ else
566+ {
567+ g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (NOT_ALLOWED));
568+ return FALSE;
569+ }
570+}
571+
572+gboolean
573+local_menu_allowed_on_window (Display *dpy, Window xid)
574+{
575+ gpointer local_menu_allowed_found = g_hash_table_lookup (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
576+
577+ if (local_menu_allowed_found)
578+ {
579+ return GPOINTER_TO_INT (local_menu_allowed_found) == ALLOWED;
580+ }
581+ else if (dpy && xid)
582+ {
583+ return read_xprop_for_window (dpy, xid);
584+ }
585+
586+ return FALSE;
587+}
588+
589+gboolean
590+gwd_window_should_have_local_menu (WnckWindow *win)
591+{
592+#ifdef META_HAS_LOCAL_MENUS
593+ const gchar * const *schemas = g_settings_list_schemas ();
594+ static GSettings *lim_settings = NULL;
595+ while (*schemas != NULL && !lim_settings)
596+ {
597+ if (g_str_equal (*schemas, "com.canonical.indicator.appmenu"))
598+ {
599+ lim_settings = g_settings_new ("com.canonical.indicator.appmenu");
600+ menu_mode = g_settings_get_enum (lim_settings, "menu-mode");
601+ g_signal_connect (lim_settings, "changed::menu-mode", G_CALLBACK (gwd_menu_mode_changed), NULL);
602+ break;
603+ }
604+ ++schemas;
605+ }
606+
607+ if (lim_settings && win)
608+ return menu_mode == LOCAL && local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), wnck_window_get_xid (win));
609+#endif
610+
611+ return FALSE;
612+}
613+
614+gchar *
615+gwd_get_entry_id_from_sync_variant (GVariant *args)
616+{
617+ /* We need to get the indicator data once we've called show */
618+
619+ GVariantIter* iter = NULL;
620+ gchar* name_hint = NULL;
621+ gchar* indicator_id = NULL;
622+ gchar* entry_id = NULL;
623+ gchar* label = NULL;
624+ gboolean label_sensitive = FALSE;
625+ gboolean label_visible = FALSE;
626+ guint32 image_type = 0;
627+ gchar* image_data = NULL;
628+ gboolean image_sensitive = FALSE;
629+ gboolean image_visible = FALSE;
630+ gint32 priority = -1;
631+
632+ g_variant_get (args, "(a(ssssbbusbbi))", &iter);
633+ while (g_variant_iter_loop (iter, "(ssssbbusbbi)",
634+ &indicator_id,
635+ &entry_id,
636+ &name_hint,
637+ &label,
638+ &label_sensitive,
639+ &label_visible,
640+ &image_type,
641+ &image_data,
642+ &image_sensitive,
643+ &image_visible,
644+ &priority))
645+ {
646+ g_variant_unref (args);
647+ return g_strdup (entry_id);
648+ }
649+
650+ g_variant_unref (args);
651+ g_assert_not_reached ();
652+ return NULL;
653+}
654+#ifdef META_HAS_LOCAL_MENUS
655+static void
656+on_local_menu_activated (GDBusProxy *proxy,
657+ gchar *sender_name,
658+ gchar *signal_name,
659+ GVariant *parameters,
660+ gpointer user_data)
661+{
662+
663+ if (g_strcmp0 (signal_name, "EntryActivated") == 0)
664+ {
665+ show_local_menu_data *d = (show_local_menu_data *) user_data;
666+ gchar *entry_id = NULL;
667+ gint x_out, y_out;
668+ guint width, height;
669+
670+ g_variant_get (parameters, "(s(iiuu))", &entry_id, &x_out, &y_out, &width, &height, NULL);
671+
672+ if (!d->local_menu_entry_id)
673+ {
674+ GError *error = NULL;
675+ GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
676+ GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
677+
678+ g_assert_no_error (error);
679+ d->local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
680+ }
681+
682+ if (g_strcmp0 (entry_id, d->local_menu_entry_id) == 0)
683+ {
684+ d->rect->x = x_out - d->dx;
685+ d->rect->y = y_out - d->dy;
686+ d->rect->width = width;
687+ d->rect->height = height;
688+ (*d->cb) (d->user_data);
689+ }
690+ else if (g_strcmp0 (entry_id, "") == 0)
691+ {
692+ memset (d->rect, 0, sizeof (GdkRectangle));
693+ (*d->cb) (d->user_data);
694+
695+ g_signal_handlers_disconnect_by_func (proxy, on_local_menu_activated, d);
696+
697+ if (active_menu)
698+ {
699+ g_free (active_menu);
700+ active_menu = NULL;
701+ }
702+
703+ g_free (d->local_menu_entry_id);
704+ g_free (d);
705+ }
706+ }
707+
708+}
709+#endif
710+gboolean
711+gwd_move_window_instead (gpointer user_data)
712+{
713+ (*pending_menu->cb) (pending_menu->user_data);
714+ g_free (pending_menu->user_data);
715+ g_free (pending_menu);
716+ pending_menu = NULL;
717+ return FALSE;
718+}
719+
720+void
721+gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
722+ gpointer user_data_start_move_window)
723+{
724+ if (pending_menu)
725+ {
726+ g_source_remove (pending_menu->move_timeout_id);
727+ g_free (pending_menu->user_data);
728+ g_free (pending_menu);
729+ pending_menu = NULL;
730+ }
731+
732+ pending_menu = g_new0 (pending_local_menu, 1);
733+ pending_menu->cb = start_move_window;
734+ pending_menu->user_data = user_data_start_move_window;
735+ pending_menu->move_timeout_id = g_timeout_add (120, gwd_move_window_instead, pending_menu);
736+}
737+
738+#ifdef META_HAS_LOCAL_MENUS
739+void
740+local_menu_entry_activated_request (GDBusProxy *proxy,
741+ gchar *sender_name,
742+ gchar *signal_name,
743+ GVariant *parameters,
744+ gpointer user_data)
745+{
746+ if (g_strcmp0 (signal_name, "EntryActivateRequest") == 0)
747+ {
748+ gchar *activated_entry_id;
749+ gchar *local_menu_entry_id;
750+ local_menu_entry_activated_request_funcs *funcs = (local_menu_entry_activated_request_funcs *) user_data;
751+
752+ g_variant_get (parameters, "(s)", &activated_entry_id, NULL);
753+
754+ GError *error = NULL;
755+ GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
756+ GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
757+
758+ g_assert_no_error (error);
759+ local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
760+
761+ if (g_strcmp0 (activated_entry_id, local_menu_entry_id) == 0)
762+ {
763+ WnckScreen *screen = wnck_screen_get_for_root (gdk_x11_get_default_root_xwindow ());
764+ int dx, dy, top_height;
765+ Window xid;
766+
767+ if (screen)
768+ {
769+ Box *rect = (*funcs->active_window_local_menu_rect_callback) ((gpointer) screen, &dx, &dy, &top_height, &xid);
770+
771+ if (rect)
772+ {
773+ int x = rect->x1;
774+ int y = top_height;
775+
776+ gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
777+ xid, x + dx, y + dy, x, y, 0, 0,
778+ funcs->show_window_menu_hidden_callback, GINT_TO_POINTER (xid));
779+ }
780+ }
781+ }
782+ }
783+}
784+#endif
785+
786+gboolean
787+gwd_show_local_menu (Display *xdisplay,
788+ Window frame_xwindow,
789+ int x,
790+ int y,
791+ int x_win,
792+ int y_win,
793+ int button,
794+ guint32 timestamp,
795+ show_window_menu_hidden_cb cb,
796+ gpointer user_data_show_window_menu)
797+{
798+ if (pending_menu)
799+ {
800+ g_source_remove (pending_menu->move_timeout_id);
801+ g_free (pending_menu->user_data);
802+ g_free (pending_menu);
803+ pending_menu = NULL;
804+ }
805+
806+#ifdef META_HAS_LOCAL_MENUS
807+
808+
809+ XUngrabPointer (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
810+ XUngrabKeyboard (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
811+ XSync (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), FALSE);
812+
813+ GDBusProxy *proxy = global_lim_listener;
814+
815+ if (proxy)
816+ {
817+ GVariant *message = g_variant_new ("(uiiu)", frame_xwindow, x, y, time);
818+ GError *error = NULL;
819+ g_dbus_proxy_call_sync (proxy, "ShowAppMenu", message, 0, 500, NULL, &error);
820+ if (error)
821+ {
822+ g_print ("error calling ShowAppMenu: %s\n", error->message);
823+ return FALSE;
824+ }
825+
826+ show_local_menu_data *data = g_new0 (show_local_menu_data, 1);
827+
828+ if (active_menu)
829+ g_free (active_menu);
830+
831+ active_menu = g_new0 (active_local_menu, 1);
832+
833+ data->cb = cb;
834+ data->user_data = user_data_show_window_menu;
835+ data->rect = &active_menu->rect;
836+ data->dx = x - x_win;
837+ data->dy = y - y_win;
838+ data->local_menu_entry_id = NULL;
839+
840+ g_signal_connect (proxy, "g-signal", G_CALLBACK (on_local_menu_activated), data);
841+
842+ return TRUE;
843+ }
844+#endif
845+ return FALSE;
846+}
847+
848+void
849+force_local_menus_on (WnckWindow *win,
850+ MetaButtonLayout *button_layout)
851+{
852+#ifdef META_HAS_LOCAL_MENUS
853+ if (gwd_window_should_have_local_menu (win))
854+ {
855+ if (button_layout->left_buttons[0] != META_BUTTON_FUNCTION_LAST)
856+ {
857+ unsigned int i = 0;
858+ for (; i < MAX_BUTTONS_PER_CORNER; i++)
859+ {
860+ if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
861+ break;
862+ else if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_LAST)
863+ {
864+ if ((i + 1) < MAX_BUTTONS_PER_CORNER)
865+ {
866+ button_layout->left_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
867+ button_layout->left_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
868+ break;
869+ }
870+ }
871+ }
872+ }
873+ if (button_layout->right_buttons[0] != META_BUTTON_FUNCTION_LAST)
874+ {
875+ unsigned int i = 0;
876+ for (; i < MAX_BUTTONS_PER_CORNER; i++)
877+ {
878+ if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
879+ break;
880+ else if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_LAST)
881+ {
882+ if ((i + 1) < MAX_BUTTONS_PER_CORNER)
883+ {
884+ button_layout->right_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
885+ button_layout->right_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
886+ break;
887+ }
888+ }
889+ }
890+ }
891+ }
892+#endif
893+}
894+
895+void
896+local_menu_cache_notify_window_destroyed (Window xid)
897+{
898+ g_hash_table_remove (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
899+}
900+
901+void
902+local_menu_cache_reload_xwindow (Display *dpy, Window xid)
903+{
904+ read_xprop_for_window (dpy, xid);
905+}
906+
907+
908
909=== added file 'gtk/window-decorator/local-menus/src/local-menus.h'
910--- gtk/window-decorator/local-menus/src/local-menus.h 1970-01-01 00:00:00 +0000
911+++ gtk/window-decorator/local-menus/src/local-menus.h 2012-02-25 05:11:17 +0000
912@@ -0,0 +1,120 @@
913+/*
914+ * Copyright © 2006 Novell, Inc.
915+ *
916+ * This library is free software; you can redistribute it and/or
917+ * modify it under the terms of the GNU Lesser General Public
918+ * License as published by the Free Software Foundation; either
919+ * version 2 of the License, or (at your option) any later version.
920+ *
921+ * This library is distributed in the hope that it will be useful,
922+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
923+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
924+ * Lesser General Public License for more details.
925+ *
926+ * You should have received a copy of the GNU Lesser General Public
927+ * License along with this library; if not, write to the
928+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
929+ * Boston, MA 02111-1307, USA.
930+ *
931+ * Author: David Reveman <davidr@novell.com>
932+ *
933+ * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
934+ * Frames Management: Copright © 2011 Canonical Ltd.
935+ * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
936+ */
937+
938+#ifdef __cplusplus
939+extern "C"
940+{
941+#endif
942+
943+#include <glib.h>
944+#include <gio/gio.h>
945+#ifndef WNCK_I_KNOW_THIS_IS_UNSTABLE
946+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
947+#endif
948+#include <libwnck/libwnck.h>
949+#include <libwnck/window-action-menu.h>
950+#include <metacity-private/theme.h>
951+#include <X11/Xregion.h>
952+
953+
954+typedef void (*show_window_menu_hidden_cb) (gpointer);
955+typedef void (*start_move_window_cb) (gpointer);
956+typedef Box * (*get_active_window_local_menu_rect_cb) (gpointer, int *, int *, int *, Window *);
957+
958+typedef struct _pending_local_menu
959+{
960+ gint move_timeout_id;
961+ gpointer user_data;
962+ start_move_window_cb cb;
963+} pending_local_menu;
964+
965+typedef struct _active_local_menu
966+{
967+ GdkRectangle rect;
968+} active_local_menu;
969+
970+extern active_local_menu *active_menu;
971+extern GDBusProxy *global_lim_listener;
972+
973+typedef struct _show_local_menu_data
974+{
975+ show_window_menu_hidden_cb cb;
976+ gpointer user_data;
977+ GdkRectangle *rect;
978+ gint dx;
979+ gint dy;
980+ gchar *local_menu_entry_id;
981+} show_local_menu_data;
982+
983+typedef struct _local_menu_entry_activated_request_funcs
984+{
985+ get_active_window_local_menu_rect_cb active_window_local_menu_rect_callback;
986+ show_window_menu_hidden_cb show_window_menu_hidden_callback;
987+} local_menu_entry_activated_request_funcs;
988+
989+gboolean
990+gwd_window_should_have_local_menu (WnckWindow *win);
991+
992+void
993+force_local_menus_on (WnckWindow *win,
994+ MetaButtonLayout *layout);
995+
996+/* Button Down */
997+void
998+gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
999+ gpointer user_data_start_move_window);
1000+
1001+/* Button Up */
1002+gboolean
1003+gwd_show_local_menu (Display *xdisplay,
1004+ Window frame_xwindow,
1005+ int x,
1006+ int y,
1007+ int x_win,
1008+ int y_win,
1009+ int button,
1010+ guint32 timestamp,
1011+ show_window_menu_hidden_cb cb,
1012+ gpointer user_data_show_window_menu);
1013+
1014+void
1015+local_menu_cache_notify_window_destroyed (Window xid);
1016+
1017+void
1018+local_menu_cache_reload_xwindow (Display *xdisplay, Window xid);
1019+
1020+gboolean
1021+local_menu_allowed_on_window (Display *dpy, Window xid);
1022+
1023+void
1024+local_menu_entry_activated_request (GDBusProxy *proxy,
1025+ gchar *sender_name,
1026+ gchar *signal_name,
1027+ GVariant *parameters,
1028+ gpointer user_data);
1029+
1030+#ifdef __cplusplus
1031+}
1032+#endif
1033
1034=== added directory 'gtk/window-decorator/local-menus/tests'
1035=== added file 'gtk/window-decorator/local-menus/tests/CMakeLists.txt'
1036--- gtk/window-decorator/local-menus/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
1037+++ gtk/window-decorator/local-menus/tests/CMakeLists.txt 2012-02-25 05:11:17 +0000
1038@@ -0,0 +1,3 @@
1039+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
1040+add_subdirectory (check_local_menu_on_off)
1041+add_subdirectory (force_local_menu_on)
1042
1043=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off'
1044=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt'
1045--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 1970-01-01 00:00:00 +0000
1046+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 2012-02-25 05:11:17 +0000
1047@@ -0,0 +1,21 @@
1048+include_directories (${CMAKE_CURRENT_SOURCE_DIR}
1049+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
1050+ ${compiz_SOURCE_DIR}/gtk/window-decorator
1051+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
1052+
1053+add_executable(
1054+ gtk_window_decorator_check_local_menu_on_off_test
1055+
1056+ ${CMAKE_CURRENT_SOURCE_DIR}/test-local-menu-on-off.cpp
1057+)
1058+
1059+target_link_libraries(
1060+ gtk_window_decorator_check_local_menu_on_off_test
1061+
1062+ gtk_window_decorator_local_menus
1063+
1064+ ${GTEST_BOTH_LIBRARIES}
1065+ ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
1066+)
1067+
1068+add_test (gtk_window_decorator_local_menus_on_off gtk_window_decorator_check_local_menu_on_off_test)
1069
1070=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off'
1071=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off/CMakeFiles'
1072=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp'
1073--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 1970-01-01 00:00:00 +0000
1074+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 2012-02-25 05:11:17 +0000
1075@@ -0,0 +1,27 @@
1076+#include "test-local-menu.h"
1077+
1078+#define GLOBAL 0
1079+#define LOCAL 1
1080+#ifdef META_HAS_LOCAL_MENUS
1081+
1082+TEST_F (GtkWindowDecoratorTestLocalMenu, TestOn)
1083+{
1084+ g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
1085+ gboolean result = gwd_window_should_have_local_menu (getWindow ());
1086+
1087+ EXPECT_TRUE (result);
1088+}
1089+
1090+TEST_F (GtkWindowDecoratorTestLocalMenu, TestOff)
1091+{
1092+ g_settings_set_enum (getSettings (), "menu-mode", GLOBAL);
1093+ gboolean result = gwd_window_should_have_local_menu (getWindow ());
1094+
1095+ EXPECT_FALSE (result);
1096+}
1097+#else
1098+TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
1099+{
1100+ ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
1101+}
1102+#endif
1103
1104=== added directory 'gtk/window-decorator/local-menus/tests/force_local_menu_on'
1105=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt'
1106--- gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 1970-01-01 00:00:00 +0000
1107+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 2012-02-25 05:11:17 +0000
1108@@ -0,0 +1,21 @@
1109+include_directories (${CMAKE_CURRENT_SOURCE_DIR}
1110+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
1111+ ${compiz_SOURCE_DIR}/gtk/window-decorator
1112+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
1113+
1114+add_executable(
1115+ gtk_window_decorator_force_local_menu_on_test
1116+
1117+ ${CMAKE_CURRENT_SOURCE_DIR}/test-force-local-menu-on.cpp
1118+)
1119+
1120+target_link_libraries(
1121+ gtk_window_decorator_force_local_menu_on_test
1122+
1123+ gtk_window_decorator_local_menus
1124+
1125+ ${GTEST_BOTH_LIBRARIES}
1126+ ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
1127+)
1128+
1129+add_test (gtk_window_decorator_force_local_menu_on gtk_window_decorator_force_local_menu_on_test)
1130
1131=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp'
1132--- gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 1970-01-01 00:00:00 +0000
1133+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 2012-02-25 05:11:17 +0000
1134@@ -0,0 +1,77 @@
1135+#include "test-local-menu.h"
1136+#include <cstring>
1137+
1138+#define GLOBAL 0
1139+#define LOCAL 1
1140+#ifdef META_HAS_LOCAL_MENUS
1141+
1142+namespace
1143+{
1144+ void initializeMetaButtonLayout (MetaButtonLayout *layout)
1145+ {
1146+ memset (layout, 0, sizeof (MetaButtonLayout));
1147+
1148+ unsigned int i;
1149+
1150+ for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
1151+ {
1152+ layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1153+ layout->left_buttons[i] = META_BUTTON_FUNCTION_LAST;
1154+ }
1155+ }
1156+}
1157+
1158+class GtkWindowDecoratorTestLocalMenuLayout :
1159+ public GtkWindowDecoratorTestLocalMenu
1160+{
1161+ public:
1162+
1163+ MetaButtonLayout * getLayout () { return &mLayout; }
1164+
1165+ virtual void SetUp ()
1166+ {
1167+ GtkWindowDecoratorTestLocalMenu::SetUp ();
1168+ ::initializeMetaButtonLayout (&mLayout);
1169+ g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
1170+ }
1171+
1172+ private:
1173+
1174+ MetaButtonLayout mLayout;
1175+};
1176+
1177+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoButtonsSet)
1178+{
1179+ force_local_menus_on (getWindow (), getLayout ());
1180+
1181+ EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
1182+ EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
1183+}
1184+
1185+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseButtonSet)
1186+{
1187+ getLayout ()->right_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
1188+
1189+ force_local_menus_on (getWindow (), getLayout ());
1190+
1191+ EXPECT_EQ (getLayout ()->right_buttons[1], META_BUTTON_FUNCTION_WINDOW_MENU);
1192+ EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
1193+}
1194+
1195+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseMinimizeMaximizeButtonSet)
1196+{
1197+ getLayout ()->left_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
1198+ getLayout ()->left_buttons[1] = META_BUTTON_FUNCTION_CLOSE;
1199+ getLayout ()->left_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
1200+
1201+ force_local_menus_on (getWindow (), getLayout ());
1202+
1203+ EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
1204+ EXPECT_EQ (getLayout ()->left_buttons[3], META_BUTTON_FUNCTION_WINDOW_MENU);
1205+}
1206+#else
1207+TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
1208+{
1209+ ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
1210+}
1211+#endif
1212
1213=== added directory 'gtk/window-decorator/local-menus/tests/show_hide_menu'
1214=== added file 'gtk/window-decorator/local-menus/tests/test-local-menu.h'
1215--- gtk/window-decorator/local-menus/tests/test-local-menu.h 1970-01-01 00:00:00 +0000
1216+++ gtk/window-decorator/local-menus/tests/test-local-menu.h 2012-02-25 05:11:17 +0000
1217@@ -0,0 +1,49 @@
1218+#include <gtest/gtest.h>
1219+#include <gmock/gmock.h>
1220+#include "local-menus.h"
1221+#include <X11/Xlib.h>
1222+#include <gio/gio.h>
1223+
1224+class GtkWindowDecoratorTestLocalMenu :
1225+ public ::testing::Test
1226+{
1227+ public:
1228+
1229+#ifdef META_HAS_LOCAL_MENUS
1230+ WnckWindow * getWindow () { return mWindow; }
1231+ GSettings * getSettings () { return mSettings; }
1232+ virtual void SetUp ()
1233+ {
1234+ gtk_init (NULL, NULL);
1235+
1236+ mXDisplay = XOpenDisplay (NULL);
1237+ mXWindow = XCreateSimpleWindow (mXDisplay, DefaultRootWindow (mXDisplay), 0, 0, 100, 100, 0, 0, 0);
1238+
1239+ XMapRaised (mXDisplay, mXWindow);
1240+
1241+ XFlush (mXDisplay);
1242+
1243+ while (g_main_context_iteration (g_main_context_default (), FALSE));
1244+
1245+ mWindow = wnck_window_get (mXWindow);
1246+
1247+ g_setenv("GSETTINGS_BACKEND", "memory", true);
1248+ mSettings = g_settings_new ("com.canonical.indicator.appmenu");
1249+ }
1250+
1251+ virtual void TearDown ()
1252+ {
1253+ XDestroyWindow (mXDisplay, mXWindow);
1254+ XCloseDisplay (mXDisplay);
1255+
1256+ g_object_unref (mSettings);
1257+ }
1258+#endif
1259+
1260+ private:
1261+
1262+ WnckWindow *mWindow;
1263+ Window mXWindow;
1264+ Display *mXDisplay;
1265+ GSettings *mSettings;
1266+};
1267
1268=== modified file 'gtk/window-decorator/metacity.c'
1269--- gtk/window-decorator/metacity.c 2012-02-16 06:51:37 +0000
1270+++ gtk/window-decorator/metacity.c 2012-02-25 05:11:17 +0000
1271@@ -24,6 +24,7 @@
1272 */
1273
1274 #include "gtk-window-decorator.h"
1275+#include "local-menus.h"
1276
1277 #ifdef USE_METACITY
1278
1279@@ -488,6 +489,8 @@
1280 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1281 }
1282
1283+ force_local_menus_on (d->win, button_layout);
1284+
1285 *flags = 0;
1286
1287 if (d->actions & WNCK_WINDOW_ACTION_CLOSE)
1288@@ -538,6 +541,12 @@
1289 *flags |= (MetaFrameFlags ) META_FRAME_ABOVE;
1290 #endif
1291
1292+#ifdef META_HAS_LOCAL_MENUS
1293+ if (d->win &&
1294+ local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), wnck_window_get_xid (d->win)))
1295+ *flags |= (MetaFrameFlags ) META_FRAME_ALLOWS_WINDOW_MENU;
1296+#endif
1297+
1298 meta_theme_get_frame_borders (theme,
1299 frame_type,
1300 d->frame->text_height,
1301@@ -558,6 +567,15 @@
1302 else
1303 clip->height = d->border_layout.left.y2 - d->border_layout.left.y1;
1304
1305+ memset (fgeom, 0, sizeof (MetaFrameGeometry));
1306+
1307+#ifdef META_HAS_LOCAL_MENUS
1308+
1309+ if (d->layout)
1310+ fgeom->text_layout = g_object_ref (d->layout);
1311+
1312+#endif
1313+
1314 meta_theme_calc_geometry (theme,
1315 frame_type,
1316 d->frame->text_height,
1317@@ -567,6 +585,15 @@
1318 button_layout,
1319 fgeom);
1320
1321+#ifdef META_HAS_LOCAL_MENUS
1322+
1323+ if (d->layout)
1324+ g_object_unref (fgeom->text_layout);
1325+
1326+ fgeom->text_layout = NULL;
1327+
1328+#endif
1329+
1330 clip->width += left_width + right_width;
1331 clip->height += top_height + bottom_height;
1332 }
1333@@ -601,6 +628,11 @@
1334 GdkColor bg_color;
1335 double bg_alpha;
1336
1337+ memset (&button_layout, 0, sizeof (MetaButtonLayout));
1338+
1339+ for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
1340+ button_layout.left_buttons[i] = button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1341+
1342 if (!d->pixmap || !d->picture)
1343 return;
1344
1345@@ -675,6 +707,9 @@
1346
1347 size = MAX (fgeom.top_height, fgeom.bottom_height);
1348
1349+ if (active_menu)
1350+ g_object_set_data (G_OBJECT (style_window), "local_menu_rect", &active_menu->rect);
1351+
1352 if (rect.width && size)
1353 {
1354 XRenderPictFormat *format;
1355@@ -899,6 +934,9 @@
1356 XRenderFreePicture (xdisplay, src);
1357 }
1358
1359+ if (active_menu)
1360+ g_object_set_data (G_OBJECT (style_window), "local_menu_rect", NULL);
1361+
1362 copy_to_front_buffer (d);
1363
1364 if (d->frame_window)
1365@@ -1078,6 +1116,15 @@
1366 break;
1367 #endif
1368
1369+#ifdef META_HAS_LOCAL_MENUS
1370+ case BUTTON_WINDOW_MENU:
1371+ if (!meta_button_present (&button_layout, META_BUTTON_FUNCTION_WINDOW_MENU))
1372+ return FALSE;
1373+
1374+ space = &fgeom.window_menu_rect;
1375+ break;
1376+#endif
1377+
1378 default:
1379 return FALSE;
1380 }
1381@@ -1266,6 +1313,8 @@
1382 MetaTheme *theme;
1383 GdkRectangle clip;
1384
1385+ memset (&fgeom, 0, sizeof (MetaFrameGeometry));
1386+
1387 theme = meta_theme_get_current ();
1388
1389 meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout,
1390@@ -1490,6 +1539,11 @@
1391 return META_BUTTON_FUNCTION_UNSTICK;
1392 #endif
1393
1394+#ifdef META_HAS_LOCAL_MENUS
1395+ else if (strcmp (str, "window_menu") == 0)
1396+ return META_BUTTON_FUNCTION_WINDOW_MENU;
1397+#endif
1398+
1399 else
1400 return META_BUTTON_FUNCTION_LAST;
1401 }
1402
1403=== modified file 'gtk/window-decorator/wnck.c'
1404--- gtk/window-decorator/wnck.c 2012-02-10 10:23:27 +0000
1405+++ gtk/window-decorator/wnck.c 2012-02-25 05:11:17 +0000
1406@@ -24,6 +24,7 @@
1407 */
1408
1409 #include "gtk-window-decorator.h"
1410+#include "local-menus.h"
1411
1412 const gchar *
1413 get_frame_type (WnckWindow *win)
1414@@ -738,7 +739,8 @@
1415 stick_button_event,
1416 unshade_button_event,
1417 unabove_button_event,
1418- unstick_button_event
1419+ unstick_button_event,
1420+ window_menu_button_event
1421 };
1422
1423 d = calloc (1, sizeof (decor_t));
1424@@ -784,6 +786,8 @@
1425 window_closed (WnckScreen *screen,
1426 WnckWindow *win)
1427 {
1428+ local_menu_cache_notify_window_destroyed (wnck_window_get_xid (win));
1429+
1430 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
1431
1432 if (d)

Subscribers

People subscribed via source and target branches