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

Proposed by Sam Spilsbury
Status: Merged
Merged at revision: 3036
Proposed branch: lp:~smspillaz/compiz-core/compiz-core.lim
Merge into: lp:compiz-core
Diff against target: 1601 lines (+1218/-11)
16 files modified
gtk/window-decorator/CMakeLists.txt (+6/-0)
gtk/window-decorator/decorator.c (+32/-6)
gtk/window-decorator/events.c (+117/-3)
gtk/window-decorator/gtk-window-decorator.c (+59/-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 (+438/-0)
gtk/window-decorator/local-menus/src/local-menus.h (+125/-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 (+47/-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 (+89/-0)
gtk/window-decorator/local-menus/tests/test-local-menu.h (+106/-0)
gtk/window-decorator/metacity.c (+54/-0)
gtk/window-decorator/wnck.c (+32/-1)
To merge this branch: bzr merge lp:~smspillaz/compiz-core/compiz-core.lim
Reviewer Review Type Date Requested Status
Daniel van Vugt Approve
Alan Griffiths Approve
Marco Trevisan (Treviño) Approve
Sam Spilsbury Pending
Review via email: mp+94891@code.launchpad.net

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

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 : Posted in a previous version of this proposal

No obvious errors

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

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

Ack.

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

Crash fixed, it looks good to me now.

Unfortunately not all the apps yet supports the _UBUNTU_APPMENU_UNIQUE_NAME atom... So maybe for testing it could be temporary disabled (sorry). A temporary workaround could be this one: http://is.gd/IXPV4h

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) :
review: Approve
3003. By Sam Spilsbury

Tell us if we have them

Revision history for this message
Alan Griffiths (alan-griffiths) :
review: Approve
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.

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

No obvious problems or regressions. And Valgrind is happy.

review: Approve

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-03-02 02:56: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-03-02 02:56: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 ? wnck_window_get_xid (win) : 0))
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-03-02 02:56: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, info->x_root, info->y_root);
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@@ -918,6 +1026,7 @@
271 xid = (gulong)
272 g_hash_table_lookup (frame_table,
273 GINT_TO_POINTER (xevent->xbutton.window));
274+ XAllowEvents (gdk_x11_display_get_xdisplay (gdkdisplay), AsyncPointer, CurrentTime);
275 break;
276 case EnterNotify:
277 case LeaveNotify:
278@@ -929,6 +1038,7 @@
279 xid = (gulong)
280 g_hash_table_lookup (frame_table,
281 GINT_TO_POINTER (xevent->xmotion.window));
282+ local_menu_process_motion (xevent->xmotion.x_root, xevent->xmotion.y_root);
283 break;
284 case PropertyNotify:
285 if (xevent->xproperty.atom == frame_input_window_atom)
286@@ -1031,6 +1141,10 @@
287 if (get_window_prop (xevent->xproperty.window, select_window_atom, &select))
288 update_switcher_window (xevent->xproperty.window, select);
289 }
290+ else if (xevent->xproperty.atom == ubuntu_appmenu_unique_name_atom)
291+ {
292+ local_menu_cache_reload_xwindow (gdk_x11_display_get_xdisplay (gdkdisplay), xevent->xproperty.window);
293+ }
294 break;
295 case DestroyNotify:
296 g_hash_table_remove (frame_table,
297
298=== modified file 'gtk/window-decorator/gtk-window-decorator.c'
299--- gtk/window-decorator/gtk-window-decorator.c 2011-10-13 12:22:14 +0000
300+++ gtk/window-decorator/gtk-window-decorator.c 2012-03-02 02:56:17 +0000
301@@ -24,6 +24,7 @@
302 */
303
304 #include "gtk-window-decorator.h"
305+#include "local-menus.h"
306
307 gboolean minimal = FALSE;
308
309@@ -48,6 +49,7 @@
310 Atom toolkit_action_atom;
311 Atom toolkit_action_window_menu_atom;
312 Atom toolkit_action_force_quit_dialog_atom;
313+Atom ubuntu_appmenu_unique_name_atom;
314
315 Atom net_wm_state_atom;
316 Atom net_wm_state_modal_atom;
317@@ -128,6 +130,38 @@
318 "normal", "modal_dialog", "dialog", "menu", "utility"
319 };
320
321+Box *
322+get_active_window_local_menu_rectangle (gpointer user_data, int *dx, int *dy, int *top_height, Window *xid)
323+{
324+ WnckScreen *screen = (WnckScreen *) user_data;
325+ WnckWindow *window = wnck_screen_get_active_window (screen);
326+ int width, height;
327+ decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
328+
329+ if (!d->decorated)
330+ return NULL;
331+
332+ wnck_window_get_geometry (window, dx, dy, &width, &height);
333+
334+ *top_height = d->context->extents.top;
335+
336+ *xid = wnck_window_get_xid (window);
337+
338+ Box *rect = &d->button_windows[BUTTON_WINDOW_MENU].pos;
339+
340+ return rect;
341+}
342+
343+void
344+on_local_menu_window_menu_updated (gpointer user_data)
345+{
346+ Window xid = GPOINTER_TO_INT (user_data);
347+ WnckWindow *window = wnck_window_get (xid);
348+ decor_t *d = g_object_get_data (G_OBJECT (window), "decor");
349+
350+ queue_decor_draw (d);
351+}
352+
353 int
354 main (int argc, char *argv[])
355 {
356@@ -304,6 +338,7 @@
357
358 net_wm_state_atom = XInternAtom (xdisplay,"_NET_WM_STATE", 0);
359 net_wm_state_modal_atom = XInternAtom (xdisplay, "_NET_WM_STATE_MODAL", 0);
360+ ubuntu_appmenu_unique_name_atom = XInternAtom (xdisplay, "_UBUNTU_APPMENU_UNIQUE_NAME", 0);
361
362 status = decor_acquire_dm_session (xdisplay,
363 gdk_screen_get_number (gdkscreen),
364@@ -448,8 +483,32 @@
365
366 update_default_decorations (gdkscreen);
367
368+#ifdef META_HAS_LOCAL_MENUS
369+ GDBusConnection *conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
370+ local_menu_entry_activated_request_funcs funcs =
371+ {
372+ get_active_window_local_menu_rectangle,
373+ on_local_menu_window_menu_updated
374+ };
375+
376+ if (conn)
377+ {
378+ global_lim_listener = g_dbus_proxy_new_sync (conn, 0, NULL, "com.canonical.Unity.Panel.Service",
379+ "/com/canonical/Unity/Panel/Service",
380+ "com.canonical.Unity.Panel.Service",
381+ NULL, NULL);
382+
383+ g_signal_connect (G_OBJECT (global_lim_listener), "g-signal", G_CALLBACK (local_menu_entry_activated_request), (gpointer) &funcs);
384+ }
385+#endif
386 gtk_main ();
387+#ifdef META_HAS_LOCAL_MENUS
388+ if (global_lim_listener)
389+ g_object_unref (global_lim_listener);
390
391+ if (conn)
392+ g_object_unref (conn);
393+#endif
394 win = windows = wnck_screen_get_windows (screen);
395
396 while (win != NULL)
397
398=== modified file 'gtk/window-decorator/gtk-window-decorator.h'
399--- gtk/window-decorator/gtk-window-decorator.h 2011-10-13 12:22:14 +0000
400+++ gtk/window-decorator/gtk-window-decorator.h 2012-03-02 02:56:17 +0000
401@@ -306,6 +306,7 @@
402 extern Atom toolkit_action_force_quit_dialog_atom;
403 extern Atom net_wm_state_atom;
404 extern Atom net_wm_state_modal_atom;
405+extern Atom ubuntu_appmenu_unique_name_atom;
406
407 extern Time dm_sn_timestamp;
408
409@@ -328,7 +329,8 @@
410 #define BUTTON_UNSHADE 7
411 #define BUTTON_UNABOVE 8
412 #define BUTTON_UNSTICK 9
413-#define BUTTON_NUM 10
414+#define BUTTON_WINDOW_MENU 10
415+#define BUTTON_NUM 11
416
417 struct _pos {
418 int x, y, w, h;
419@@ -1013,6 +1015,11 @@
420 decor_event_type gtkwd_type);
421
422 void
423+window_menu_button_event (WnckWindow *win,
424+ decor_event *gtkwd_event,
425+ decor_event_type gtkwd_type);
426+
427+void
428 handle_title_button_event (WnckWindow *win,
429 int action,
430 decor_event *gtkwd_event);
431
432=== added directory 'gtk/window-decorator/local-menus'
433=== added file 'gtk/window-decorator/local-menus/CMakeLists.txt'
434--- gtk/window-decorator/local-menus/CMakeLists.txt 1970-01-01 00:00:00 +0000
435+++ gtk/window-decorator/local-menus/CMakeLists.txt 2012-03-02 02:56:17 +0000
436@@ -0,0 +1,60 @@
437+pkg_check_modules(
438+ LOCAL_MENUS
439+ REQUIRED
440+ glib-2.0 gio-2.0 libwnck-1.0 gtk+-2.0>=2.18.0
441+)
442+
443+INCLUDE_DIRECTORIES(
444+ ${CMAKE_CURRENT_SOURCE_DIR}/include
445+ ${CMAKE_CURRENT_SOURCE_DIR}/src
446+
447+ ${compiz_SOURCE_DIR}/gtk/window-decorator
448+
449+ ${Boost_INCLUDE_DIRS}
450+
451+ ${LOCAL_MENUS_INCLUDE_DIRS}
452+ ${METACITY_INCLUDE_DIRS}
453+)
454+
455+LINK_DIRECTORIES (${LOCAL_MENUS_LIBRARY_DIRS})
456+
457+SET(
458+ PUBLIC_HEADERS
459+)
460+
461+SET(
462+ PRIVATE_HEADERS
463+ ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.h
464+)
465+
466+SET(
467+ SRCS
468+ ${CMAKE_CURRENT_SOURCE_DIR}/src/local-menus.c
469+)
470+
471+ADD_LIBRARY(
472+ gtk_window_decorator_local_menus STATIC
473+
474+ ${SRCS}
475+
476+ ${PUBLIC_HEADERS}
477+ ${PRIVATE_HEADERS}
478+)
479+
480+IF (COMPIZ_BUILD_TESTING)
481+ADD_SUBDIRECTORY( ${CMAKE_CURRENT_SOURCE_DIR}/tests )
482+ENDIF (COMPIZ_BUILD_TESTING)
483+
484+SET_TARGET_PROPERTIES(
485+ gtk_window_decorator_local_menus PROPERTIES
486+ PUBLIC_HEADER "${PUBLIC_HEADERS}"
487+)
488+
489+install (FILES ${PUBLIC_HEADERS} DESTINATION ${COMPIZ_CORE_INCLUDE_DIR})
490+
491+TARGET_LINK_LIBRARIES(
492+ gtk_window_decorator_local_menus
493+
494+ ${LOCAL_MENUS_LIBRARIES}
495+)
496+
497
498=== added directory 'gtk/window-decorator/local-menus/include'
499=== added directory 'gtk/window-decorator/local-menus/src'
500=== added file 'gtk/window-decorator/local-menus/src/local-menus.c'
501--- gtk/window-decorator/local-menus/src/local-menus.c 1970-01-01 00:00:00 +0000
502+++ gtk/window-decorator/local-menus/src/local-menus.c 2012-03-02 02:56:17 +0000
503@@ -0,0 +1,438 @@
504+/*
505+ * Copyright © 2006 Novell, Inc.
506+ *
507+ * This library is free software; you can redistribute it and/or
508+ * modify it under the terms of the GNU Lesser General Public
509+ * License as published by the Free Software Foundation; either
510+ * version 2 of the License, or (at your option) any later version.
511+ *
512+ * This library is distributed in the hope that it will be useful,
513+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
514+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
515+ * Lesser General Public License for more details.
516+ *
517+ * You should have received a copy of the GNU Lesser General Public
518+ * License along with this library; if not, write to the
519+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
520+ * Boston, MA 02111-1307, USA.
521+ *
522+ * Author: David Reveman <davidr@novell.com>
523+ *
524+ * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
525+ * Frames Management: Copright © 2011 Canonical Ltd.
526+ * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
527+ */
528+
529+#include <string.h>
530+#include "local-menus.h"
531+#include <gdk/gdk.h>
532+#include <gdk/gdkx.h>
533+#include <stdlib.h>
534+
535+#define GLOBAL 0
536+#define LOCAL 1
537+
538+#define ALLOWED 2
539+#define NOT_ALLOWED 1
540+
541+gint menu_mode = GLOBAL;
542+
543+GDBusProxy *global_lim_listener;
544+
545+
546+#ifdef META_HAS_LOCAL_MENUS
547+static void
548+gwd_menu_mode_changed (GSettings *settings,
549+ gchar *key,
550+ gpointer user_data)
551+{
552+ menu_mode = g_settings_get_enum (settings, "menu-mode");
553+}
554+#endif
555+
556+active_local_menu *active_menu;
557+pending_local_menu *pending_menu;
558+
559+GHashTable *get_windows_with_menus_table ()
560+{
561+ static GHashTable *windows_with_menus = NULL;
562+
563+ if (!windows_with_menus)
564+ windows_with_menus = g_hash_table_new (NULL, NULL);
565+
566+ return windows_with_menus;
567+}
568+
569+static gboolean read_xprop_for_window (Display *dpy, Window xid)
570+{
571+ Atom ubuntu_appmenu_unique_name = XInternAtom (dpy, "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
572+ Atom utf8_string = XInternAtom (dpy, "UTF8_STRING", FALSE);
573+ Atom actual;
574+ int fmt;
575+ unsigned long nitems, nleft;
576+ unsigned char *prop;
577+ XGetWindowProperty (dpy, xid, ubuntu_appmenu_unique_name,
578+ 0L, 16L, FALSE, utf8_string, &actual, &fmt, &nitems, &nleft, &prop);
579+
580+ if (actual == utf8_string && fmt == 8 && nitems > 1)
581+ {
582+ g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (ALLOWED));
583+ return TRUE;
584+ }
585+ else
586+ {
587+ g_hash_table_replace (get_windows_with_menus_table (), GINT_TO_POINTER (xid), GINT_TO_POINTER (NOT_ALLOWED));
588+ return FALSE;
589+ }
590+}
591+
592+gboolean
593+local_menu_allowed_on_window (Display *dpy, Window xid)
594+{
595+ gpointer local_menu_allowed_found = g_hash_table_lookup (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
596+
597+ if (local_menu_allowed_found)
598+ {
599+ return GPOINTER_TO_INT (local_menu_allowed_found) == ALLOWED;
600+ }
601+ else if (dpy && xid)
602+ {
603+ return read_xprop_for_window (dpy, xid);
604+ }
605+
606+ return FALSE;
607+}
608+
609+gboolean
610+gwd_window_should_have_local_menu (Window win)
611+{
612+#ifdef META_HAS_LOCAL_MENUS
613+ const gchar * const *schemas = g_settings_list_schemas ();
614+ static GSettings *lim_settings = NULL;
615+ while (*schemas != NULL && !lim_settings)
616+ {
617+ if (g_str_equal (*schemas, "com.canonical.indicator.appmenu"))
618+ {
619+ lim_settings = g_settings_new ("com.canonical.indicator.appmenu");
620+ menu_mode = g_settings_get_enum (lim_settings, "menu-mode");
621+ g_signal_connect (lim_settings, "changed::menu-mode", G_CALLBACK (gwd_menu_mode_changed), NULL);
622+ break;
623+ }
624+ ++schemas;
625+ }
626+
627+ if (lim_settings && win)
628+ return menu_mode == LOCAL && local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), win);
629+#endif
630+
631+ return FALSE;
632+}
633+
634+gchar *
635+gwd_get_entry_id_from_sync_variant (GVariant *args)
636+{
637+ /* We need to get the indicator data once we've called show */
638+
639+ GVariantIter* iter = NULL;
640+ gchar* name_hint = NULL;
641+ gchar* indicator_id = NULL;
642+ gchar* entry_id = NULL;
643+ gchar* label = NULL;
644+ gboolean label_sensitive = FALSE;
645+ gboolean label_visible = FALSE;
646+ guint32 image_type = 0;
647+ gchar* image_data = NULL;
648+ gboolean image_sensitive = FALSE;
649+ gboolean image_visible = FALSE;
650+ gint32 priority = -1;
651+
652+ g_variant_get (args, "(a(ssssbbusbbi))", &iter);
653+ while (g_variant_iter_loop (iter, "(ssssbbusbbi)",
654+ &indicator_id,
655+ &entry_id,
656+ &name_hint,
657+ &label,
658+ &label_sensitive,
659+ &label_visible,
660+ &image_type,
661+ &image_data,
662+ &image_sensitive,
663+ &image_visible,
664+ &priority))
665+ {
666+ g_variant_unref (args);
667+ return g_strdup (entry_id);
668+ }
669+
670+ g_variant_unref (args);
671+ g_assert_not_reached ();
672+ return NULL;
673+}
674+#ifdef META_HAS_LOCAL_MENUS
675+static void
676+on_local_menu_activated (GDBusProxy *proxy,
677+ gchar *sender_name,
678+ gchar *signal_name,
679+ GVariant *parameters,
680+ gpointer user_data)
681+{
682+
683+ if (g_strcmp0 (signal_name, "EntryActivated") == 0)
684+ {
685+ show_local_menu_data *d = (show_local_menu_data *) user_data;
686+ gchar *entry_id = NULL;
687+ gint x_out, y_out;
688+ guint width, height;
689+
690+ g_variant_get (parameters, "(s(iiuu))", &entry_id, &x_out, &y_out, &width, &height, NULL);
691+
692+ if (!d->local_menu_entry_id)
693+ {
694+ GError *error = NULL;
695+ GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
696+ GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
697+
698+ g_assert_no_error (error);
699+ d->local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
700+ }
701+
702+ if (g_strcmp0 (entry_id, d->local_menu_entry_id) == 0)
703+ {
704+ d->rect->x = x_out - d->dx;
705+ d->rect->y = y_out - d->dy;
706+ d->rect->width = width;
707+ d->rect->height = height;
708+ (*d->cb) (d->user_data);
709+ }
710+ else if (g_strcmp0 (entry_id, "") == 0)
711+ {
712+ memset (d->rect, 0, sizeof (GdkRectangle));
713+ (*d->cb) (d->user_data);
714+
715+ g_signal_handlers_disconnect_by_func (proxy, on_local_menu_activated, d);
716+
717+ if (active_menu)
718+ {
719+ g_free (active_menu);
720+ active_menu = NULL;
721+ }
722+
723+ g_free (d->local_menu_entry_id);
724+ g_free (d);
725+ }
726+ }
727+
728+}
729+#endif
730+gboolean
731+gwd_move_window_instead (gpointer user_data)
732+{
733+ (*pending_menu->cb) (pending_menu->user_data);
734+ g_source_remove (pending_menu->move_timeout_id);
735+ g_free (pending_menu->user_data);
736+ g_free (pending_menu);
737+ pending_menu = NULL;
738+ return FALSE;
739+}
740+
741+void
742+local_menu_process_motion(gint x_root, gint y_root)
743+{
744+ if (!pending_menu)
745+ return;
746+
747+ if (abs (pending_menu->x_root - x_root) > 4 &&
748+ abs (pending_menu->y_root - y_root) > 4)
749+ gwd_move_window_instead (pending_menu);
750+}
751+
752+void
753+gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
754+ gpointer user_data_start_move_window,
755+ gint x_root,
756+ gint y_root)
757+{
758+ if (pending_menu)
759+ {
760+ g_source_remove (pending_menu->move_timeout_id);
761+ g_free (pending_menu->user_data);
762+ g_free (pending_menu);
763+ pending_menu = NULL;
764+ }
765+
766+ pending_menu = g_new0 (pending_local_menu, 1);
767+ pending_menu->cb = start_move_window;
768+ pending_menu->user_data = user_data_start_move_window;
769+ pending_menu->move_timeout_id = g_timeout_add (150, gwd_move_window_instead, pending_menu);
770+}
771+
772+#ifdef META_HAS_LOCAL_MENUS
773+void
774+local_menu_entry_activated_request (GDBusProxy *proxy,
775+ gchar *sender_name,
776+ gchar *signal_name,
777+ GVariant *parameters,
778+ gpointer user_data)
779+{
780+ if (g_strcmp0 (signal_name, "EntryActivateRequest") == 0)
781+ {
782+ gchar *activated_entry_id;
783+ gchar *local_menu_entry_id;
784+ local_menu_entry_activated_request_funcs *funcs = (local_menu_entry_activated_request_funcs *) user_data;
785+
786+ g_variant_get (parameters, "(s)", &activated_entry_id, NULL);
787+
788+ GError *error = NULL;
789+ GVariant *params = g_variant_new ("(s)", "libappmenu.so", NULL);
790+ GVariant *args = g_dbus_proxy_call_sync (proxy, "SyncOne", params, 0, 500, NULL, &error);
791+
792+ g_assert_no_error (error);
793+ local_menu_entry_id = gwd_get_entry_id_from_sync_variant (args);
794+
795+ if (g_strcmp0 (activated_entry_id, local_menu_entry_id) == 0)
796+ {
797+ WnckScreen *screen = wnck_screen_get_for_root (gdk_x11_get_default_root_xwindow ());
798+ int dx, dy, top_height;
799+ Window xid;
800+
801+ if (screen)
802+ {
803+ Box *rect = (*funcs->active_window_local_menu_rect_callback) ((gpointer) screen, &dx, &dy, &top_height, &xid);
804+
805+ if (rect)
806+ {
807+ int x = rect->x1;
808+ int y = top_height;
809+
810+ gwd_show_local_menu (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
811+ xid, x + dx, y + dy, x, y, 0, 0,
812+ funcs->show_window_menu_hidden_callback, GINT_TO_POINTER (xid));
813+ }
814+ }
815+ }
816+ }
817+}
818+#endif
819+
820+gboolean
821+gwd_show_local_menu (Display *xdisplay,
822+ Window frame_xwindow,
823+ int x,
824+ int y,
825+ int x_win,
826+ int y_win,
827+ int button,
828+ guint32 timestamp,
829+ show_window_menu_hidden_cb cb,
830+ gpointer user_data_show_window_menu)
831+{
832+ if (pending_menu)
833+ {
834+ g_source_remove (pending_menu->move_timeout_id);
835+ g_free (pending_menu->user_data);
836+ g_free (pending_menu);
837+ pending_menu = NULL;
838+ }
839+
840+#ifdef META_HAS_LOCAL_MENUS
841+
842+
843+ XUngrabPointer (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
844+ XUngrabKeyboard (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), CurrentTime);
845+ XSync (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), FALSE);
846+
847+ GDBusProxy *proxy = global_lim_listener;
848+
849+ if (proxy)
850+ {
851+ GVariant *message = g_variant_new ("(uiiu)", frame_xwindow, x, y, time);
852+ GError *error = NULL;
853+ g_dbus_proxy_call_sync (proxy, "ShowAppMenu", message, 0, 500, NULL, &error);
854+ if (error)
855+ {
856+ g_print ("error calling ShowAppMenu: %s\n", error->message);
857+ return FALSE;
858+ }
859+
860+ show_local_menu_data *data = g_new0 (show_local_menu_data, 1);
861+
862+ if (active_menu)
863+ g_free (active_menu);
864+
865+ active_menu = g_new0 (active_local_menu, 1);
866+
867+ data->cb = cb;
868+ data->user_data = user_data_show_window_menu;
869+ data->rect = &active_menu->rect;
870+ data->dx = x - x_win;
871+ data->dy = y - y_win;
872+ data->local_menu_entry_id = NULL;
873+
874+ g_signal_connect (proxy, "g-signal", G_CALLBACK (on_local_menu_activated), data);
875+
876+ return TRUE;
877+ }
878+#endif
879+ return FALSE;
880+}
881+
882+void
883+force_local_menus_on (Window win,
884+ MetaButtonLayout *button_layout)
885+{
886+#ifdef META_HAS_LOCAL_MENUS
887+ if (gwd_window_should_have_local_menu (win))
888+ {
889+ if (button_layout->left_buttons[0] != META_BUTTON_FUNCTION_LAST)
890+ {
891+ unsigned int i = 0;
892+ for (; i < MAX_BUTTONS_PER_CORNER; i++)
893+ {
894+ if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
895+ break;
896+ else if (button_layout->left_buttons[i] == META_BUTTON_FUNCTION_LAST)
897+ {
898+ if ((i + 1) < MAX_BUTTONS_PER_CORNER)
899+ {
900+ button_layout->left_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
901+ button_layout->left_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
902+ break;
903+ }
904+ }
905+ }
906+ }
907+ if (button_layout->right_buttons[0] != META_BUTTON_FUNCTION_LAST)
908+ {
909+ unsigned int i = 0;
910+ for (; i < MAX_BUTTONS_PER_CORNER; i++)
911+ {
912+ if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_WINDOW_MENU)
913+ break;
914+ else if (button_layout->right_buttons[i] == META_BUTTON_FUNCTION_LAST)
915+ {
916+ if ((i + 1) < MAX_BUTTONS_PER_CORNER)
917+ {
918+ button_layout->right_buttons[i + 1] = META_BUTTON_FUNCTION_LAST;
919+ button_layout->right_buttons[i] = META_BUTTON_FUNCTION_WINDOW_MENU;
920+ break;
921+ }
922+ }
923+ }
924+ }
925+ }
926+#endif
927+}
928+
929+void
930+local_menu_cache_notify_window_destroyed (Window xid)
931+{
932+ g_hash_table_remove (get_windows_with_menus_table (), GINT_TO_POINTER (xid));
933+}
934+
935+void
936+local_menu_cache_reload_xwindow (Display *dpy, Window xid)
937+{
938+ read_xprop_for_window (dpy, xid);
939+}
940+
941+
942
943=== added file 'gtk/window-decorator/local-menus/src/local-menus.h'
944--- gtk/window-decorator/local-menus/src/local-menus.h 1970-01-01 00:00:00 +0000
945+++ gtk/window-decorator/local-menus/src/local-menus.h 2012-03-02 02:56:17 +0000
946@@ -0,0 +1,125 @@
947+/*
948+ * Copyright © 2006 Novell, Inc.
949+ *
950+ * This library is free software; you can redistribute it and/or
951+ * modify it under the terms of the GNU Lesser General Public
952+ * License as published by the Free Software Foundation; either
953+ * version 2 of the License, or (at your option) any later version.
954+ *
955+ * This library is distributed in the hope that it will be useful,
956+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
957+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
958+ * Lesser General Public License for more details.
959+ *
960+ * You should have received a copy of the GNU Lesser General Public
961+ * License along with this library; if not, write to the
962+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
963+ * Boston, MA 02111-1307, USA.
964+ *
965+ * Author: David Reveman <davidr@novell.com>
966+ *
967+ * 2D Mode: Copyright © 2010 Sam Spilsbury <smspillaz@gmail.com>
968+ * Frames Management: Copright © 2011 Canonical Ltd.
969+ * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com>
970+ */
971+
972+#ifdef __cplusplus
973+extern "C"
974+{
975+#endif
976+
977+#include <glib.h>
978+#include <gio/gio.h>
979+#ifndef WNCK_I_KNOW_THIS_IS_UNSTABLE
980+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
981+#endif
982+#include <libwnck/libwnck.h>
983+#include <libwnck/window-action-menu.h>
984+#include <metacity-private/theme.h>
985+#include <X11/Xregion.h>
986+
987+
988+typedef void (*show_window_menu_hidden_cb) (gpointer);
989+typedef void (*start_move_window_cb) (gpointer);
990+typedef Box * (*get_active_window_local_menu_rect_cb) (gpointer, int *, int *, int *, Window *);
991+
992+typedef struct _pending_local_menu
993+{
994+ gint move_timeout_id;
995+ gpointer user_data;
996+ start_move_window_cb cb;
997+ gint x_root;
998+ gint y_root;
999+} pending_local_menu;
1000+
1001+typedef struct _active_local_menu
1002+{
1003+ GdkRectangle rect;
1004+} active_local_menu;
1005+
1006+extern active_local_menu *active_menu;
1007+extern GDBusProxy *global_lim_listener;
1008+
1009+typedef struct _show_local_menu_data
1010+{
1011+ show_window_menu_hidden_cb cb;
1012+ gpointer user_data;
1013+ GdkRectangle *rect;
1014+ gint dx;
1015+ gint dy;
1016+ gchar *local_menu_entry_id;
1017+} show_local_menu_data;
1018+
1019+typedef struct _local_menu_entry_activated_request_funcs
1020+{
1021+ get_active_window_local_menu_rect_cb active_window_local_menu_rect_callback;
1022+ show_window_menu_hidden_cb show_window_menu_hidden_callback;
1023+} local_menu_entry_activated_request_funcs;
1024+
1025+gboolean
1026+gwd_window_should_have_local_menu (Window win);
1027+
1028+void
1029+force_local_menus_on (Window win,
1030+ MetaButtonLayout *layout);
1031+
1032+/* Button Down */
1033+void
1034+gwd_prepare_show_local_menu (start_move_window_cb start_move_window,
1035+ gpointer user_data_start_move_window, gint x_root, gint y_root);
1036+
1037+/* Button Up */
1038+gboolean
1039+gwd_show_local_menu (Display *xdisplay,
1040+ Window frame_xwindow,
1041+ int x,
1042+ int y,
1043+ int x_win,
1044+ int y_win,
1045+ int button,
1046+ guint32 timestamp,
1047+ show_window_menu_hidden_cb cb,
1048+ gpointer user_data_show_window_menu);
1049+
1050+void
1051+local_menu_cache_notify_window_destroyed (Window xid);
1052+
1053+void
1054+local_menu_cache_reload_xwindow (Display *xdisplay, Window xid);
1055+
1056+gboolean
1057+local_menu_allowed_on_window (Display *dpy, Window xid);
1058+
1059+void
1060+local_menu_entry_activated_request (GDBusProxy *proxy,
1061+ gchar *sender_name,
1062+ gchar *signal_name,
1063+ GVariant *parameters,
1064+ gpointer user_data);
1065+
1066+void
1067+local_menu_process_motion (gint x_root, gint y_root);
1068+
1069+#ifdef __cplusplus
1070+}
1071+#endif
1072
1073=== added directory 'gtk/window-decorator/local-menus/tests'
1074=== added file 'gtk/window-decorator/local-menus/tests/CMakeLists.txt'
1075--- gtk/window-decorator/local-menus/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
1076+++ gtk/window-decorator/local-menus/tests/CMakeLists.txt 2012-03-02 02:56:17 +0000
1077@@ -0,0 +1,3 @@
1078+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
1079+add_subdirectory (check_local_menu_on_off)
1080+add_subdirectory (force_local_menu_on)
1081
1082=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off'
1083=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt'
1084--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 1970-01-01 00:00:00 +0000
1085+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/CMakeLists.txt 2012-03-02 02:56:17 +0000
1086@@ -0,0 +1,21 @@
1087+include_directories (${CMAKE_CURRENT_SOURCE_DIR}
1088+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
1089+ ${compiz_SOURCE_DIR}/gtk/window-decorator
1090+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
1091+
1092+add_executable(
1093+ gtk_window_decorator_check_local_menu_on_off_test
1094+
1095+ ${CMAKE_CURRENT_SOURCE_DIR}/test-local-menu-on-off.cpp
1096+)
1097+
1098+target_link_libraries(
1099+ gtk_window_decorator_check_local_menu_on_off_test
1100+
1101+ gtk_window_decorator_local_menus
1102+
1103+ ${GTEST_BOTH_LIBRARIES}
1104+ ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
1105+)
1106+
1107+add_test (gtk_window_decorator_local_menus_on_off gtk_window_decorator_check_local_menu_on_off_test)
1108
1109=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off'
1110=== added directory 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/check_local_menu_on_off/CMakeFiles'
1111=== added file 'gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp'
1112--- gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 1970-01-01 00:00:00 +0000
1113+++ gtk/window-decorator/local-menus/tests/check_local_menu_on_off/test-local-menu-on-off.cpp 2012-03-02 02:56:17 +0000
1114@@ -0,0 +1,47 @@
1115+#include "test-local-menu.h"
1116+#include <string.h>
1117+#include <gdk/gdkx.h>
1118+
1119+#define GLOBAL 0
1120+#define LOCAL 1
1121+#ifdef META_HAS_LOCAL_MENUS
1122+
1123+TEST_F (GtkWindowDecoratorTestLocalMenu, TestOnNoProp)
1124+{
1125+ g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
1126+ gboolean result = gwd_window_should_have_local_menu (getWindow ());
1127+
1128+ EXPECT_FALSE (result);
1129+}
1130+
1131+TEST_F (GtkWindowDecoratorTestLocalMenu, TestOnWithProp)
1132+{
1133+ g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
1134+
1135+ Window xid = getWindow ();
1136+ Atom ubuntu_appmenu_unique_name = XInternAtom (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
1137+ Atom utf8_string = XInternAtom (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), "UTF8_STRING", FALSE);
1138+ const char data[] = ":abcd1234";
1139+
1140+ XChangeProperty (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), xid, ubuntu_appmenu_unique_name, utf8_string, 8, PropModeReplace, (const unsigned char *) data, strlen (data));
1141+
1142+ gdk_display_sync (gdk_display_get_default ());
1143+
1144+ gboolean result = gwd_window_should_have_local_menu (getWindow ());
1145+
1146+ EXPECT_TRUE (result);
1147+}
1148+
1149+TEST_F (GtkWindowDecoratorTestLocalMenu, TestOff)
1150+{
1151+ g_settings_set_enum (getSettings (), "menu-mode", GLOBAL);
1152+ gboolean result = gwd_window_should_have_local_menu (getWindow ());
1153+
1154+ EXPECT_FALSE (result);
1155+}
1156+#else
1157+TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
1158+{
1159+ ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
1160+}
1161+#endif
1162
1163=== added directory 'gtk/window-decorator/local-menus/tests/force_local_menu_on'
1164=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt'
1165--- gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 1970-01-01 00:00:00 +0000
1166+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/CMakeLists.txt 2012-03-02 02:56:17 +0000
1167@@ -0,0 +1,21 @@
1168+include_directories (${CMAKE_CURRENT_SOURCE_DIR}
1169+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/tests
1170+ ${compiz_SOURCE_DIR}/gtk/window-decorator
1171+ ${compiz_SOURCE_DIR}/gtk/window-decorator/local-menus/src)
1172+
1173+add_executable(
1174+ gtk_window_decorator_force_local_menu_on_test
1175+
1176+ ${CMAKE_CURRENT_SOURCE_DIR}/test-force-local-menu-on.cpp
1177+)
1178+
1179+target_link_libraries(
1180+ gtk_window_decorator_force_local_menu_on_test
1181+
1182+ gtk_window_decorator_local_menus
1183+
1184+ ${GTEST_BOTH_LIBRARIES}
1185+ ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
1186+)
1187+
1188+add_test (gtk_window_decorator_force_local_menu_on gtk_window_decorator_force_local_menu_on_test)
1189
1190=== added file 'gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp'
1191--- gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 1970-01-01 00:00:00 +0000
1192+++ gtk/window-decorator/local-menus/tests/force_local_menu_on/test-force-local-menu-on.cpp 2012-03-02 02:56:17 +0000
1193@@ -0,0 +1,89 @@
1194+#include "test-local-menu.h"
1195+#include <cstring>
1196+#include <gdk/gdkx.h>
1197+
1198+#define GLOBAL 0
1199+#define LOCAL 1
1200+#ifdef META_HAS_LOCAL_MENUS
1201+
1202+namespace
1203+{
1204+ void initializeMetaButtonLayout (MetaButtonLayout *layout)
1205+ {
1206+ memset (layout, 0, sizeof (MetaButtonLayout));
1207+
1208+ unsigned int i;
1209+
1210+ for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
1211+ {
1212+ layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1213+ layout->left_buttons[i] = META_BUTTON_FUNCTION_LAST;
1214+ }
1215+ }
1216+}
1217+
1218+class GtkWindowDecoratorTestLocalMenuLayout :
1219+ public GtkWindowDecoratorTestLocalMenu
1220+{
1221+ public:
1222+
1223+ MetaButtonLayout * getLayout () { return &mLayout; }
1224+
1225+ virtual void SetUp ()
1226+ {
1227+ GtkWindowDecoratorTestLocalMenu::SetUp ();
1228+ ::initializeMetaButtonLayout (&mLayout);
1229+ g_settings_set_enum (getSettings (), "menu-mode", LOCAL);
1230+
1231+ Window xid = getWindow ();
1232+ Atom ubuntu_appmenu_unique_name = XInternAtom (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), "_UBUNTU_APPMENU_UNIQUE_NAME", FALSE);
1233+ Atom utf8_string = XInternAtom (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), "UTF8_STRING", FALSE);
1234+ const char data[] = ":abcd1234";
1235+
1236+ XChangeProperty (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), xid, ubuntu_appmenu_unique_name, utf8_string, 8, PropModeReplace, (const unsigned char *) data, strlen (data));
1237+
1238+ gdk_display_sync (gdk_display_get_default ());
1239+
1240+ ASSERT_TRUE (gwd_window_should_have_local_menu (getWindow ()));
1241+ }
1242+
1243+ private:
1244+
1245+ MetaButtonLayout mLayout;
1246+};
1247+
1248+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoButtonsSet)
1249+{
1250+ force_local_menus_on (getWindow (), getLayout ());
1251+
1252+ EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
1253+ EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
1254+}
1255+
1256+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseButtonSet)
1257+{
1258+ getLayout ()->right_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
1259+
1260+ force_local_menus_on (getWindow (), getLayout ());
1261+
1262+ EXPECT_EQ (getLayout ()->right_buttons[1], META_BUTTON_FUNCTION_WINDOW_MENU);
1263+ EXPECT_EQ (getLayout ()->left_buttons[0], META_BUTTON_FUNCTION_LAST);
1264+}
1265+
1266+TEST_F (GtkWindowDecoratorTestLocalMenuLayout, TestForceNoCloseMinimizeMaximizeButtonSet)
1267+{
1268+ getLayout ()->left_buttons[0] = META_BUTTON_FUNCTION_CLOSE;
1269+ getLayout ()->left_buttons[1] = META_BUTTON_FUNCTION_CLOSE;
1270+ getLayout ()->left_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
1271+
1272+ force_local_menus_on (getWindow (), getLayout ());
1273+
1274+ EXPECT_EQ (getLayout ()->right_buttons[0], META_BUTTON_FUNCTION_LAST);
1275+ EXPECT_EQ (getLayout ()->left_buttons[3], META_BUTTON_FUNCTION_WINDOW_MENU);
1276+}
1277+#else
1278+TEST_F (GtkWindowDecoratorTestLocalMenu, NoMenus)
1279+{
1280+ ASSERT_TRUE (true) << "Local menus tests not enabled because META_HAS_LOCAL_MENUS is off";
1281+}
1282+#endif
1283
1284=== added directory 'gtk/window-decorator/local-menus/tests/show_hide_menu'
1285=== added file 'gtk/window-decorator/local-menus/tests/test-local-menu.h'
1286--- gtk/window-decorator/local-menus/tests/test-local-menu.h 1970-01-01 00:00:00 +0000
1287+++ gtk/window-decorator/local-menus/tests/test-local-menu.h 2012-03-02 02:56:17 +0000
1288@@ -0,0 +1,106 @@
1289+#include <gtest/gtest.h>
1290+#include <gmock/gmock.h>
1291+#include "local-menus.h"
1292+#include <X11/Xlib.h>
1293+#include <gio/gio.h>
1294+#include <sys/poll.h>
1295+#include <X11/Xatom.h>
1296+
1297+class GtkWindowDecoratorTestLocalMenu :
1298+ public ::testing::Test
1299+{
1300+ public:
1301+
1302+#ifdef META_HAS_LOCAL_MENUS
1303+ Window getWindow () { return mXWindow; }
1304+ GSettings * getSettings () { return mSettings; }
1305+ virtual void SetUp ()
1306+ {
1307+ gtk_init (NULL, NULL);
1308+
1309+ mXDisplay = XOpenDisplay (NULL);
1310+
1311+ XSelectInput (mXDisplay, DefaultRootWindow (mXDisplay), PropertyChangeMask);
1312+
1313+ mXWindow = XCreateSimpleWindow (mXDisplay, DefaultRootWindow (mXDisplay), 0, 0, 100, 100, 0, 0, 0);
1314+
1315+ Atom net_client_list = XInternAtom (mXDisplay, "_NET_CLIENT_LIST", FALSE);
1316+
1317+ XMapRaised (mXDisplay, mXWindow);
1318+
1319+ XFlush (mXDisplay);
1320+
1321+ /* Wait for _NET_CLIENT_LIST to be updated with this window */
1322+ while (1)
1323+ {
1324+ struct pollfd pfd;
1325+
1326+ pfd.events = POLLIN;
1327+ pfd.revents = 0;
1328+ pfd.fd = ConnectionNumber (mXDisplay);
1329+
1330+ XEvent event;
1331+
1332+ poll (&pfd, 1, -1);
1333+
1334+ XNextEvent (mXDisplay, &event);
1335+
1336+ gboolean foundWindow = FALSE;
1337+
1338+ switch (event.type)
1339+ {
1340+ case PropertyNotify:
1341+ {
1342+ if (event.xproperty.atom == net_client_list)
1343+ {
1344+ Atom type;
1345+ int fmt;
1346+ unsigned long nitems, nleft;
1347+ unsigned char *prop;
1348+
1349+ XGetWindowProperty (mXDisplay, event.xproperty.window, net_client_list,
1350+ 0L, 128L, FALSE, XA_WINDOW, &type, &fmt, &nitems, &nleft, &prop);
1351+
1352+ if (fmt == 32 && type == XA_WINDOW && nitems && !nleft)
1353+ {
1354+ Window *windows = (Window *) prop;
1355+
1356+ while (nitems--)
1357+ {
1358+ if (*(windows++) == mXWindow)
1359+ foundWindow = TRUE;
1360+ }
1361+ }
1362+
1363+ XFree (prop);
1364+ }
1365+
1366+ break;
1367+ }
1368+ default:
1369+ break;
1370+ }
1371+
1372+ if (foundWindow)
1373+ break;
1374+ }
1375+
1376+ g_setenv("GSETTINGS_BACKEND", "memory", true);
1377+ mSettings = g_settings_new ("com.canonical.indicator.appmenu");
1378+ }
1379+
1380+ virtual void TearDown ()
1381+ {
1382+ XDestroyWindow (mXDisplay, mXWindow);
1383+ XCloseDisplay (mXDisplay);
1384+
1385+ g_object_unref (mSettings);
1386+ }
1387+#endif
1388+
1389+ private:
1390+
1391+ Window mXWindow;
1392+ Display *mXDisplay;
1393+ GSettings *mSettings;
1394+};
1395
1396=== modified file 'gtk/window-decorator/metacity.c'
1397--- gtk/window-decorator/metacity.c 2012-02-16 06:51:37 +0000
1398+++ gtk/window-decorator/metacity.c 2012-03-02 02:56:17 +0000
1399@@ -24,6 +24,7 @@
1400 */
1401
1402 #include "gtk-window-decorator.h"
1403+#include "local-menus.h"
1404
1405 #ifdef USE_METACITY
1406
1407@@ -488,6 +489,8 @@
1408 button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1409 }
1410
1411+ force_local_menus_on (d->win ? wnck_window_get_xid (d->win) : 0, button_layout);
1412+
1413 *flags = 0;
1414
1415 if (d->actions & WNCK_WINDOW_ACTION_CLOSE)
1416@@ -538,6 +541,12 @@
1417 *flags |= (MetaFrameFlags ) META_FRAME_ABOVE;
1418 #endif
1419
1420+#ifdef META_HAS_LOCAL_MENUS
1421+ if (d->win &&
1422+ local_menu_allowed_on_window (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), wnck_window_get_xid (d->win)))
1423+ *flags |= (MetaFrameFlags ) META_FRAME_ALLOWS_WINDOW_MENU;
1424+#endif
1425+
1426 meta_theme_get_frame_borders (theme,
1427 frame_type,
1428 d->frame->text_height,
1429@@ -558,6 +567,15 @@
1430 else
1431 clip->height = d->border_layout.left.y2 - d->border_layout.left.y1;
1432
1433+ memset (fgeom, 0, sizeof (MetaFrameGeometry));
1434+
1435+#ifdef META_HAS_LOCAL_MENUS
1436+
1437+ if (d->layout)
1438+ fgeom->text_layout = g_object_ref (d->layout);
1439+
1440+#endif
1441+
1442 meta_theme_calc_geometry (theme,
1443 frame_type,
1444 d->frame->text_height,
1445@@ -567,6 +585,15 @@
1446 button_layout,
1447 fgeom);
1448
1449+#ifdef META_HAS_LOCAL_MENUS
1450+
1451+ if (d->layout)
1452+ g_object_unref (fgeom->text_layout);
1453+
1454+ fgeom->text_layout = NULL;
1455+
1456+#endif
1457+
1458 clip->width += left_width + right_width;
1459 clip->height += top_height + bottom_height;
1460 }
1461@@ -601,6 +628,11 @@
1462 GdkColor bg_color;
1463 double bg_alpha;
1464
1465+ memset (&button_layout, 0, sizeof (MetaButtonLayout));
1466+
1467+ for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
1468+ button_layout.left_buttons[i] = button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1469+
1470 if (!d->pixmap || !d->picture)
1471 return;
1472
1473@@ -675,6 +707,9 @@
1474
1475 size = MAX (fgeom.top_height, fgeom.bottom_height);
1476
1477+ if (active_menu)
1478+ g_object_set_data (G_OBJECT (style_window), "local_menu_rect", &active_menu->rect);
1479+
1480 if (rect.width && size)
1481 {
1482 XRenderPictFormat *format;
1483@@ -899,6 +934,9 @@
1484 XRenderFreePicture (xdisplay, src);
1485 }
1486
1487+ if (active_menu)
1488+ g_object_set_data (G_OBJECT (style_window), "local_menu_rect", NULL);
1489+
1490 copy_to_front_buffer (d);
1491
1492 if (d->frame_window)
1493@@ -1078,6 +1116,15 @@
1494 break;
1495 #endif
1496
1497+#ifdef META_HAS_LOCAL_MENUS
1498+ case BUTTON_WINDOW_MENU:
1499+ if (!meta_button_present (&button_layout, META_BUTTON_FUNCTION_WINDOW_MENU))
1500+ return FALSE;
1501+
1502+ space = &fgeom.window_menu_rect;
1503+ break;
1504+#endif
1505+
1506 default:
1507 return FALSE;
1508 }
1509@@ -1266,6 +1313,8 @@
1510 MetaTheme *theme;
1511 GdkRectangle clip;
1512
1513+ memset (&fgeom, 0, sizeof (MetaFrameGeometry));
1514+
1515 theme = meta_theme_get_current ();
1516
1517 meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout,
1518@@ -1490,6 +1539,11 @@
1519 return META_BUTTON_FUNCTION_UNSTICK;
1520 #endif
1521
1522+#ifdef META_HAS_LOCAL_MENUS
1523+ else if (strcmp (str, "window_menu") == 0)
1524+ return META_BUTTON_FUNCTION_WINDOW_MENU;
1525+#endif
1526+
1527 else
1528 return META_BUTTON_FUNCTION_LAST;
1529 }
1530
1531=== modified file 'gtk/window-decorator/wnck.c'
1532--- gtk/window-decorator/wnck.c 2012-02-10 10:23:27 +0000
1533+++ gtk/window-decorator/wnck.c 2012-03-02 02:56:17 +0000
1534@@ -24,6 +24,7 @@
1535 */
1536
1537 #include "gtk-window-decorator.h"
1538+#include "local-menus.h"
1539
1540 const gchar *
1541 get_frame_type (WnckWindow *win)
1542@@ -375,6 +376,19 @@
1543 if (cursor[i][j].cursor)
1544 XDefineCursor (xdisplay, d->event_windows[i][j].window,
1545 cursor[i][j].cursor);
1546+
1547+ XGrabButton (xdisplay,
1548+ Button1Mask,
1549+ AnyModifier,
1550+ d->event_windows[i][j].window,
1551+ FALSE,
1552+ ButtonPressMask |
1553+ ButtonReleaseMask |
1554+ PointerMotionMask,
1555+ GrabModeAsync,
1556+ GrabModeAsync,
1557+ None,
1558+ None);
1559 }
1560 }
1561
1562@@ -389,6 +403,20 @@
1563 CopyFromParent, CopyFromParent, CopyFromParent,
1564 CWOverrideRedirect | CWEventMask, &attr);
1565
1566+
1567+ XGrabButton (xdisplay,
1568+ Button1Mask,
1569+ AnyModifier,
1570+ d->button_windows[i].window,
1571+ FALSE,
1572+ ButtonPressMask |
1573+ ButtonReleaseMask |
1574+ PointerMotionMask,
1575+ GrabModeAsync,
1576+ GrabModeAsync,
1577+ None,
1578+ None);
1579+
1580 d->button_states[i] = 0;
1581 }
1582 }
1583@@ -738,7 +766,8 @@
1584 stick_button_event,
1585 unshade_button_event,
1586 unabove_button_event,
1587- unstick_button_event
1588+ unstick_button_event,
1589+ window_menu_button_event
1590 };
1591
1592 d = calloc (1, sizeof (decor_t));
1593@@ -784,6 +813,8 @@
1594 window_closed (WnckScreen *screen,
1595 WnckWindow *win)
1596 {
1597+ local_menu_cache_notify_window_destroyed (wnck_window_get_xid (win));
1598+
1599 decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
1600
1601 if (d)

Subscribers

People subscribed via source and target branches