Compare commits
	
		
			1 Commits
		
	
	
		
			wip/compos
			...
			wip/resize
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | ffe7b5e7d8 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -48,6 +48,7 @@ po/*.pot | ||||
| 50-metacity-key.xml | ||||
| libmutter-wayland.pc | ||||
| mutter-wayland | ||||
| mutter-launch | ||||
| org.gnome.mutter.gschema.valid | ||||
| org.gnome.mutter.gschema.xml | ||||
| org.gnome.mutter.wayland.gschema.valid | ||||
| @@ -76,7 +77,6 @@ src/mutter-marshal.[ch] | ||||
| src/stamp-mutter-marshal.h | ||||
| src/meta-dbus-xrandr.[ch] | ||||
| src/meta-dbus-idle-monitor.[ch] | ||||
| src/meta-dbus-login1.[ch] | ||||
| src/mutter-plugins.pc | ||||
| src/wayland/gtk-shell-protocol.c | ||||
| src/wayland/gtk-shell-server-protocol.h | ||||
|   | ||||
							
								
								
									
										14
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,17 +1,3 @@ | ||||
| 3.11.91 | ||||
| ======= | ||||
| * Don't use keysym to match keybindings [Rui; #678001] | ||||
| * Fix message tray icons showing up blank (again) [Adel; #725180] | ||||
| * Improve keybinding lookups [Rui; #725588] | ||||
| * Fix dynamic updates of titlebar style properties [Owen; #725751] | ||||
| * Fix positioning of manually positioned windows [Owen; #724049] | ||||
| * Misc bug fixes and cleanups [Jasper, Carlos, Adel, Giovanni, Florian; #720631, | ||||
|   #724969, #725216, #724402, #722266, #725338, #725525] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner, | ||||
|   Jasper St. Pierre, Owen W. Taylor | ||||
|  | ||||
| 3.11.90 | ||||
| ======= | ||||
| * Fix double-scaling on high DPI resolutions [Adel; #723931] | ||||
|   | ||||
							
								
								
									
										10
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ AC_CONFIG_MACRO_DIR([m4]) | ||||
|  | ||||
| m4_define([mutter_major_version], [3]) | ||||
| m4_define([mutter_minor_version], [11]) | ||||
| m4_define([mutter_micro_version], [91]) | ||||
| m4_define([mutter_micro_version], [90]) | ||||
|  | ||||
| m4_define([mutter_version], | ||||
|           [mutter_major_version.mutter_minor_version.mutter_micro_version]) | ||||
| @@ -140,6 +140,12 @@ AM_GLIB_GNU_GETTEXT | ||||
| ## here we get the flags we'll actually use | ||||
| # GRegex requires Glib-2.14.0 | ||||
| PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0) | ||||
| PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login) | ||||
|  | ||||
| saved_LIBS="$LIBS" | ||||
| LIBS="$LIBS $MUTTER_LAUNCH" | ||||
| AC_CHECK_FUNCS([sd_session_get_vt]) | ||||
| LIBS="$saved_LIBS" | ||||
|  | ||||
| # Unconditionally use this dir to avoid a circular dep with gnomecc | ||||
| GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings" | ||||
| @@ -212,7 +218,7 @@ AS_IF([test "x$WAYLAND_SCANNER" = "xno"], | ||||
| AC_SUBST([WAYLAND_SCANNER]) | ||||
| AC_SUBST(XWAYLAND_PATH) | ||||
|  | ||||
| MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server libdrm libsystemd" | ||||
| MUTTER_PC_MODULES="$MUTTER_PC_MODULES clutter-wayland-1.0 clutter-wayland-compositor-1.0 clutter-egl-1.0 wayland-server libdrm" | ||||
| PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES) | ||||
|  | ||||
| PKG_CHECK_EXISTS([xi >= 1.6.99.1], | ||||
|   | ||||
| @@ -40,22 +40,19 @@ | ||||
|  | ||||
|     <enum name="version"> | ||||
|       <description summary="latest protocol version"> | ||||
| 	The 'current' member of this enum gives the version of the | ||||
| 	protocol.  Implementations can compare this to the version | ||||
| 	they implement using static_assert to ensure the protocol and | ||||
| 	implementation versions match. | ||||
| 	Use this enum to check the protocol version, and it will be updated | ||||
| 	automatically. | ||||
|       </description> | ||||
|       <entry name="current" value="3" summary="Always the latest version"/> | ||||
|       <entry name="current" value="2" summary="Always the latest version"/> | ||||
|     </enum> | ||||
|  | ||||
|  | ||||
|     <request name="use_unstable_version"> | ||||
|       <description summary="enable use of this unstable version"> | ||||
| 	Negotiate the unstable version of the interface.  This | ||||
| 	mechanism is in place to ensure client and server agree on the | ||||
| 	unstable versions of the protocol that they speak or exit | ||||
| 	cleanly if they don't agree.  This request will go away once | ||||
| 	the xdg-shell protocol is stable. | ||||
| 	Use this request in order to enable use of this interface. | ||||
|  | ||||
| 	Understand and agree that one is using an unstable interface, | ||||
| 	that will likely change in the future, breaking the API. | ||||
|       </description> | ||||
|       <arg name="version" type="int"/> | ||||
|     </request> | ||||
| @@ -201,37 +198,6 @@ | ||||
|       <arg name="app_id" type="string"/> | ||||
|     </request> | ||||
|  | ||||
|     <enum name="surface_type"> | ||||
|       <description summary="surface types"> | ||||
|         Surface | ||||
|       </description> | ||||
|       <entry name="normal" value="0" summary="A normal window. This is the default type."/> | ||||
|       <entry name="modal_dialog" value="1" summary="A modal dialog."/> | ||||
|     </enum> | ||||
|  | ||||
|     <request name="set_surface_type"> | ||||
|       <description summary="set the surface type"> | ||||
|         Sets the type of the surface. These are high-level "roles" of | ||||
|         surfaces designed to give the compositor more heuristics about | ||||
|         how to place and manage the window. | ||||
|       </description> | ||||
|       <arg name="surface_type" type="uint" summary="a surface type from surface_type"/> | ||||
|     </request> | ||||
|  | ||||
|     <request name="show_window_menu"> | ||||
|       <description summary="show the window menu"> | ||||
|         Clients implementing client-side decorations might want to show | ||||
|         a context menu when right-clicking on the decorations, giving the | ||||
|         user a menu that they can use to maximize or minimize the window. | ||||
|  | ||||
|         The seat passed must have either pointer or keyboard focus to pop | ||||
|         up the window menu for a surface. | ||||
|       </description> | ||||
|  | ||||
|       <arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/> | ||||
|       <arg name="serial" type="uint" summary="serial of the event to pop up the window for"/> | ||||
|     </request> | ||||
|  | ||||
|     <request name="move"> | ||||
|       <description summary="start an interactive move"> | ||||
| 	Start a pointer-driven move of the surface. | ||||
| @@ -309,87 +275,113 @@ | ||||
|       <arg name="output" type="object" interface="wl_output" allow-null="true"/> | ||||
|     </request> | ||||
|  | ||||
|     <enum name="state"> | ||||
|       <description summary="types of state on the surface"> | ||||
|         The different state values used on the surface. This is designed for | ||||
|         state values like maximized, fullscreen. It is paired with the | ||||
|         request_change_state event to ensure that both the client and the | ||||
|         compositor setting the state can be synchronized. | ||||
|  | ||||
|         States set in this way are double-buffered. They will get applied on | ||||
|         the next commit. | ||||
|  | ||||
|         Desktop environments may extend this enum by taking up a range of | ||||
|         values and documenting the range they chose in this description. | ||||
|         They are not required to document the values for the range that they | ||||
|         chose. Ideally, any good extensions from a desktop environment should | ||||
|         make its way into standardization into this enum. | ||||
|  | ||||
|         The current reserved ranges are: | ||||
|  | ||||
|         0x0000 - 0x0FFF: xdg-shell core values, documented below. | ||||
|         0x1000 - 0x1FFF: GNOME | ||||
|     <event name="request_set_fullscreen"> | ||||
|       <description summary="server requests that the client set fullscreen"> | ||||
| 	Event sent from the compositor to the client requesting that the client | ||||
| 	goes to a fullscreen state. It's the client job to call set_fullscreen | ||||
| 	and really trigger the fullscreen state. | ||||
|       </description> | ||||
|       <entry name="maximized" value="1" summary="the surface is maximized"> | ||||
|         A non-zero value indicates the surface is maximized. Otherwise, | ||||
|         the surface is unmaximized. | ||||
|       </entry> | ||||
|       <entry name="fullscreen" value="2" summary="the surface is fullscreen"> | ||||
|         A non-zero value indicates the surface is fullscreen. Otherwise, | ||||
|         the surface is not fullscreen. | ||||
|       </entry> | ||||
|     </enum> | ||||
|  | ||||
|     <request name="request_change_state"> | ||||
|       <description summary="client requests to change a surface's state"> | ||||
|         This asks the compositor to change the state. If the compositor wants | ||||
|         to change the state, it will send a change_state event with the same | ||||
|         state_type, value, and serial, and the event flow continues as if it | ||||
|         it was initiated by the compositor. | ||||
|  | ||||
|         If the compositor does not want to change the state, it will send a | ||||
|         change_state to the client with the old value of the state. | ||||
|       </description> | ||||
|       <arg name="state_type" type="uint" summary="the state to set"/> | ||||
|       <arg name="value" type="uint" summary="the value to change the state to"/> | ||||
|       <arg name="serial" type="uint" summary="an event serial"> | ||||
|         This serial is so the client can know which change_state event corresponds | ||||
|         to which request_change_state request it sent out. | ||||
|       </arg> | ||||
|     </request> | ||||
|  | ||||
|     <event name="change_state"> | ||||
|       <description summary="compositor wants to change a surface's state"> | ||||
|         This event tells the client to change a surface's state. The client | ||||
|         should respond with an ack_change_state request to the compositor to | ||||
|         guarantee that the compositor knows that the client has seen it. | ||||
|       </description> | ||||
|  | ||||
|       <arg name="state_type" type="uint" summary="the state to set"/> | ||||
|       <arg name="value" type="uint" summary="the value to change the state to"/> | ||||
|       <arg name="serial" type="uint" summary="a serial for the compositor's own tracking"/> | ||||
|     </event> | ||||
|  | ||||
|     <request name="ack_change_state"> | ||||
|       <description summary="ack a change_state event"> | ||||
|         When a change_state event is received, a client should then ack it | ||||
|         using the ack_change_state request to ensure that the compositor | ||||
|         knows the client has seen the event. | ||||
|  | ||||
|         By this point, the state is confirmed, and the next attach should | ||||
|         contain the buffer drawn for the new state value. | ||||
|  | ||||
|         The values here need to be the same as the values in the cooresponding | ||||
|         change_state event. | ||||
|     <event name="request_unset_fullscreen"> | ||||
|       <description summary="server requests that the client unset fullscreen"> | ||||
| 	Event sent from the compositor to the client requesting that the client | ||||
| 	leaves the fullscreen state. It's the client job to call | ||||
| 	unset_fullscreen and really leave the fullscreen state. | ||||
|       </description> | ||||
|     </event> | ||||
|  | ||||
|     <request name="set_fullscreen"> | ||||
|       <description summary="set the surface state as fullscreen"> | ||||
| 	Set the surface as fullscreen. | ||||
|  | ||||
| 	After this request, the compositor should send a configure event | ||||
| 	informing the output size. | ||||
|  | ||||
| 	This request informs the compositor that the next attached buffer | ||||
| 	committed will be in a fullscreen state. The buffer size should be the | ||||
| 	same size as the size informed in the configure event, if the client | ||||
| 	doesn't want to leave any empty area. | ||||
|  | ||||
| 	In other words: the next attached buffer after set_maximized is the new | ||||
| 	maximized buffer. And the surface will be positioned at the maximized | ||||
| 	position on commit. | ||||
|  | ||||
| 	A simple way to synchronize and wait for the correct configure event is | ||||
| 	to use a wl_display.sync request right after the set_fullscreen | ||||
| 	request. When the sync callback returns, the last configure event | ||||
| 	received just before it will be the correct one, and should contain the | ||||
| 	right size for the surface to maximize. | ||||
|  | ||||
| 	Setting one state won't unset another state. Use | ||||
| 	xdg_surface.unset_fullscreen for unsetting it. | ||||
|       </description> | ||||
|     </request> | ||||
|  | ||||
|     <request name="unset_fullscreen"> | ||||
|       <description summary="unset the surface state as fullscreen"> | ||||
| 	Unset the surface fullscreen state. | ||||
|  | ||||
| 	Same negotiation as set_fullscreen must be used. | ||||
|       </description> | ||||
|     </request> | ||||
|  | ||||
|     <event name="request_set_maximized"> | ||||
|       <description summary="server requests that the client set maximized"> | ||||
| 	Event sent from the compositor to the client requesting that the client | ||||
| 	goes to a maximized state. It's the client job to call set_maximized | ||||
| 	and really trigger the maximized state. | ||||
|       </description> | ||||
|     </event> | ||||
|  | ||||
|     <event name="request_unset_maximized"> | ||||
|       <description summary="server requests that the client unset maximized"> | ||||
| 	Event sent from the compositor to the client requesting that the client | ||||
| 	leaves the maximized state. It's the client job to call unset_maximized | ||||
| 	and really leave the maximized state. | ||||
|       </description> | ||||
|     </event> | ||||
|  | ||||
|     <request name="set_maximized"> | ||||
|       <description summary="set the surface state as maximized"> | ||||
| 	Set the surface as maximized. | ||||
|  | ||||
| 	After this request, the compositor will send a configure event | ||||
| 	informing the output size minus panel and other MW decorations. | ||||
|  | ||||
| 	This request informs the compositor that the next attached buffer | ||||
| 	committed will be in a maximized state. The buffer size should be the | ||||
| 	same size as the size informed in the configure event, if the client | ||||
| 	doesn't want to leave any empty area. | ||||
|  | ||||
| 	In other words: the next attached buffer after set_maximized is the new | ||||
| 	maximized buffer. And the surface will be positioned at the maximized | ||||
| 	position on commit. | ||||
|  | ||||
| 	A simple way to synchronize and wait for the correct configure event is | ||||
| 	to use a wl_display.sync request right after the set_maximized request. | ||||
| 	When the sync callback returns, the last configure event received just | ||||
| 	before it will be the correct one, and should contain the right size | ||||
| 	for the surface to maximize. | ||||
|  | ||||
| 	Setting one state won't unset another state. Use | ||||
| 	xdg_surface.unset_maximized for unsetting it. | ||||
|       </description> | ||||
|     </request> | ||||
|  | ||||
|     <request name="unset_maximized"> | ||||
|       <description summary="unset the surface state as maximized"> | ||||
| 	Unset the surface maximized state. | ||||
|  | ||||
| 	Same negotiation as set_maximized must be used. | ||||
|       </description> | ||||
|       <arg name="state_type" type="uint" summary="the state to set"/> | ||||
|       <arg name="value" type="uint" summary="the value to change the state to"/> | ||||
|       <arg name="serial" type="uint" summary="a serial to pass to change_state"/> | ||||
|     </request> | ||||
|  | ||||
|     <request name="set_minimized"> | ||||
|       <description summary="minimize the surface"> | ||||
|         Minimize the surface. | ||||
|       <description summary="set the surface state as minimized"> | ||||
| 	Set the surface minimized state. | ||||
|  | ||||
| 	Setting one state won't unset another state. | ||||
|       </description> | ||||
|     </request> | ||||
|  | ||||
|   | ||||
| @@ -39,7 +39,6 @@ INCLUDES += \ | ||||
| mutter_built_sources = \ | ||||
| 	$(dbus_idle_built_sources)		\ | ||||
| 	$(dbus_xrandr_built_sources)		\ | ||||
| 	$(dbus_login1_built_sources)		\ | ||||
| 	mutter-enum-types.h 			\ | ||||
| 	mutter-enum-types.c			\ | ||||
| 	wayland/gtk-shell-protocol.c		\ | ||||
| @@ -178,6 +177,10 @@ libmutter_wayland_la_SOURCES =			\ | ||||
| 	ui/ui.h					\ | ||||
| 	ui/frames.c				\ | ||||
| 	ui/frames.h				\ | ||||
| 	ui/menu.c				\ | ||||
| 	ui/menu.h				\ | ||||
| 	ui/metaaccellabel.c			\ | ||||
| 	ui/metaaccellabel.h			\ | ||||
| 	ui/resizepopup.c			\ | ||||
| 	ui/resizepopup.h			\ | ||||
| 	ui/theme-parser.c			\ | ||||
| @@ -204,8 +207,8 @@ libmutter_wayland_la_SOURCES =			\ | ||||
| 	wayland/meta-wayland-surface.h		\ | ||||
| 	wayland/meta-wayland-types.h		\ | ||||
| 	wayland/meta-wayland-versions.h		\ | ||||
| 	wayland/meta-login1.c			\ | ||||
| 	wayland/meta-login1.h | ||||
| 	wayland/meta-weston-launch.c		\ | ||||
| 	wayland/meta-weston-launch.h | ||||
|  | ||||
| nodist_libmutter_wayland_la_SOURCES =		\ | ||||
| 	$(mutter_built_sources) | ||||
| @@ -260,6 +263,17 @@ bin_PROGRAMS=mutter-wayland | ||||
| mutter_wayland_SOURCES = core/mutter.c | ||||
| mutter_wayland_LDADD = $(MUTTER_LIBS) libmutter-wayland.la | ||||
|  | ||||
| bin_PROGRAMS+=mutter-launch | ||||
|  | ||||
| mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h | ||||
|  | ||||
| mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS) -DLIBDIR=\"$(libdir)\" | ||||
| mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam | ||||
|  | ||||
| install-exec-hook: | ||||
| 	-chown root $(DESTDIR)$(bindir)/mutter-launch | ||||
| 	-chmod u+s $(DESTDIR)$(bindir)/mutter-launch | ||||
|  | ||||
| if HAVE_INTROSPECTION | ||||
| include $(INTROSPECTION_MAKEFILE) | ||||
|  | ||||
| @@ -395,15 +409,6 @@ $(dbus_idle_built_sources) : Makefile.am idle-monitor.xml | ||||
| 		--c-generate-object-manager						\ | ||||
| 		$(srcdir)/idle-monitor.xml | ||||
|  | ||||
| dbus_login1_built_sources = meta-dbus-login1.c meta-dbus-login1.h | ||||
|  | ||||
| $(dbus_login1_built_sources) : Makefile.am org.freedesktop.login1.xml | ||||
| 	$(AM_V_GEN)gdbus-codegen							\ | ||||
| 		--interface-prefix org.freedesktop.login1				\ | ||||
| 		--c-namespace Login1							\ | ||||
| 		--generate-c-code meta-dbus-login1					\ | ||||
| 		$(srcdir)/org.freedesktop.login1.xml | ||||
|  | ||||
| wayland/%-protocol.c : $(top_builddir)/protocol/%.xml | ||||
| 	mkdir -p wayland | ||||
| 	$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@ | ||||
|   | ||||
| @@ -436,6 +436,45 @@ begin_modal_x11 (MetaScreen       *screen, | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| begin_modal_wayland (MetaScreen       *screen, | ||||
|                      MetaPlugin       *plugin, | ||||
|                      MetaModalOptions  options, | ||||
|                      guint32           timestamp) | ||||
| { | ||||
|   MetaWaylandCompositor *compositor; | ||||
|   gboolean pointer_grabbed = FALSE; | ||||
|   gboolean keyboard_grabbed = FALSE; | ||||
|  | ||||
|   compositor = meta_wayland_compositor_get_default (); | ||||
|  | ||||
|   if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0) | ||||
|     { | ||||
|       if (!meta_wayland_pointer_begin_modal (&compositor->seat->pointer)) | ||||
|         goto fail; | ||||
|  | ||||
|       pointer_grabbed = TRUE; | ||||
|     } | ||||
|   if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0) | ||||
|     { | ||||
|       if (!meta_wayland_keyboard_begin_modal (&compositor->seat->keyboard, | ||||
|                                               timestamp)) | ||||
|         goto fail; | ||||
|  | ||||
|       keyboard_grabbed = TRUE; | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
|  | ||||
|  fail: | ||||
|   if (pointer_grabbed) | ||||
|     meta_wayland_pointer_end_modal (&compositor->seat->pointer); | ||||
|   if (keyboard_grabbed) | ||||
|     meta_wayland_keyboard_end_modal (&compositor->seat->keyboard, timestamp); | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_begin_modal_for_plugin (MetaScreen       *screen, | ||||
|                              MetaPlugin       *plugin, | ||||
| @@ -454,7 +493,7 @@ meta_begin_modal_for_plugin (MetaScreen       *screen, | ||||
|     return FALSE; | ||||
|  | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     ok = TRUE; | ||||
|     ok = begin_modal_wayland (screen, plugin, options, timestamp); | ||||
|   else | ||||
|     ok = begin_modal_x11 (screen, plugin, options, timestamp); | ||||
|   if (!ok) | ||||
| @@ -482,7 +521,15 @@ meta_end_modal_for_plugin (MetaScreen     *screen, | ||||
|  | ||||
|   g_return_if_fail (compositor->modal_plugin == plugin); | ||||
|  | ||||
|   if (!meta_is_wayland_compositor ()) | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     { | ||||
|       MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); | ||||
|  | ||||
|       meta_wayland_pointer_end_modal (&compositor->seat->pointer); | ||||
|       meta_wayland_keyboard_end_modal (&compositor->seat->keyboard, | ||||
|                                        timestamp); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp); | ||||
|       XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp); | ||||
| @@ -1723,15 +1770,3 @@ meta_compositor_hide_tile_preview (MetaCompositor *compositor, | ||||
|  | ||||
|   meta_plugin_manager_hide_tile_preview (info->plugin_mgr); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_compositor_show_window_menu (MetaCompositor *compositor, | ||||
|                                   MetaWindow     *window) | ||||
| { | ||||
|   MetaCompScreen *info = meta_screen_get_compositor_data (window->screen); | ||||
|  | ||||
|   if (!info->plugin_mgr) | ||||
|     return; | ||||
|  | ||||
|   meta_plugin_manager_show_window_menu (info->plugin_mgr, window); | ||||
| } | ||||
|   | ||||
| @@ -365,18 +365,3 @@ meta_plugin_manager_hide_tile_preview (MetaPluginManager *plugin_mgr) | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_plugin_manager_show_window_menu (MetaPluginManager *plugin_mgr, | ||||
|                                       MetaWindow        *window) | ||||
| { | ||||
|   MetaPlugin *plugin = plugin_mgr->plugin; | ||||
|   MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); | ||||
|   MetaDisplay *display  = meta_screen_get_display (plugin_mgr->screen); | ||||
|  | ||||
|   if (display->display_opening) | ||||
|     return; | ||||
|  | ||||
|   if (klass->show_window_menu) | ||||
|     klass->show_window_menu (plugin, window); | ||||
| } | ||||
|   | ||||
| @@ -80,8 +80,4 @@ gboolean meta_plugin_manager_show_tile_preview (MetaPluginManager *mgr, | ||||
|                                                 MetaRectangle     *tile_rect, | ||||
|                                                 int                tile_monitor_number); | ||||
| gboolean meta_plugin_manager_hide_tile_preview (MetaPluginManager *mgr); | ||||
|  | ||||
| void meta_plugin_manager_show_window_menu (MetaPluginManager *mgr, | ||||
|                                            MetaWindow        *window); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -714,7 +714,7 @@ meta_window_actor_has_shadow (MetaWindowActor *self) | ||||
|   /* Leaving out shadows for maximized and fullscreen windows is an effeciency | ||||
|    * win and also prevents the unsightly effect of the shadow of maximized | ||||
|    * window appearing on an adjacent window */ | ||||
|   if ((meta_window_get_maximized (priv->window) == META_MAXIMIZE_BOTH) || | ||||
|   if ((meta_window_get_maximized (priv->window) == (META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL)) || | ||||
|       meta_window_is_fullscreen (priv->window)) | ||||
|     return FALSE; | ||||
|  | ||||
|   | ||||
| @@ -425,9 +425,8 @@ setup_constraint_info (ConstraintInfo      *info, | ||||
|    * the monitor. | ||||
|    */ | ||||
|   if (meta_prefs_get_force_fullscreen() && | ||||
|       window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND && | ||||
|       !window->hide_titlebar_when_maximized && | ||||
|       (window->decorated || !meta_window_is_client_decorated (window)) && | ||||
|       window->decorated && | ||||
|       meta_rectangle_equal (new, &monitor_info->rect) && | ||||
|       window->has_fullscreen_func && | ||||
|       !window->fullscreen) | ||||
| @@ -492,17 +491,12 @@ place_window_if_needed(MetaWindow     *window, | ||||
|       !window->minimized && | ||||
|       !window->fullscreen) | ||||
|     { | ||||
|       MetaRectangle orig_rect; | ||||
|       MetaRectangle placed_rect; | ||||
|       MetaWorkspace *cur_workspace; | ||||
|       const MetaMonitorInfo *monitor_info; | ||||
|  | ||||
|       meta_window_get_frame_rect (window, &placed_rect); | ||||
|  | ||||
|       orig_rect = info->orig; | ||||
|       extend_by_frame (window, &orig_rect); | ||||
|  | ||||
|       meta_window_place (window, orig_rect.x, orig_rect.y, | ||||
|       meta_window_place (window, info->orig.x, info->orig.y, | ||||
|                          &placed_rect.x, &placed_rect.y); | ||||
|       did_placement = TRUE; | ||||
|  | ||||
| @@ -817,7 +811,7 @@ constrain_maximization (MetaWindow         *window, | ||||
|                                                 active_workspace_struts); | ||||
|    } | ||||
|   /* Now make target_size = maximized size of client window */ | ||||
|   /* unextend_by_frame (window, &target_size); */ | ||||
|   unextend_by_frame (window, &target_size); | ||||
|  | ||||
|   /* Check min size constraints; max size constraints are ignored for maximized | ||||
|    * windows, as per bug 327543. | ||||
|   | ||||
							
								
								
									
										143
									
								
								src/core/core.c
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								src/core/core.c
									
									
									
									
									
								
							| @@ -328,7 +328,8 @@ meta_core_maximize (Display *xdisplay, | ||||
|   if (meta_prefs_get_raise_on_click ()) | ||||
|     meta_window_raise (window); | ||||
|  | ||||
|   meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|   meta_window_maximize (window,  | ||||
|                         META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -341,9 +342,11 @@ meta_core_toggle_maximize_vertically (Display *xdisplay, | ||||
|     meta_window_raise (window); | ||||
|  | ||||
|   if (META_WINDOW_MAXIMIZED_VERTICALLY (window)) | ||||
|     meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL); | ||||
|     meta_window_unmaximize (window,  | ||||
|                             META_MAXIMIZE_VERTICAL); | ||||
|   else | ||||
|     meta_window_maximize (window, META_MAXIMIZE_VERTICAL); | ||||
|     meta_window_maximize (window, | ||||
|     			    META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -356,9 +359,11 @@ meta_core_toggle_maximize_horizontally (Display *xdisplay, | ||||
|     meta_window_raise (window); | ||||
|  | ||||
|   if (META_WINDOW_MAXIMIZED_HORIZONTALLY (window)) | ||||
|     meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL); | ||||
|     meta_window_unmaximize (window,  | ||||
|                             META_MAXIMIZE_HORIZONTAL); | ||||
|   else | ||||
|     meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL); | ||||
|     meta_window_maximize (window, | ||||
|     			    META_MAXIMIZE_HORIZONTAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -371,9 +376,11 @@ meta_core_toggle_maximize (Display *xdisplay, | ||||
|     meta_window_raise (window); | ||||
|  | ||||
|   if (META_WINDOW_MAXIMIZED (window)) | ||||
|     meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_unmaximize (window,  | ||||
|                             META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
|   else | ||||
|     meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_maximize (window, | ||||
|                           META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -385,7 +392,8 @@ meta_core_unmaximize (Display *xdisplay, | ||||
|   if (meta_prefs_get_raise_on_click ()) | ||||
|     meta_window_raise (window); | ||||
|  | ||||
|   meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|   meta_window_unmaximize (window, | ||||
|                           META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -469,6 +477,9 @@ meta_core_change_workspace (Display *xdisplay, | ||||
| void | ||||
| meta_core_show_window_menu (Display *xdisplay, | ||||
|                             Window   frame_xwindow, | ||||
|                             int      root_x, | ||||
|                             int      root_y, | ||||
|                             int      button, | ||||
|                             guint32  timestamp) | ||||
| { | ||||
|   MetaWindow *window = get_window (xdisplay, frame_xwindow); | ||||
| @@ -477,7 +488,121 @@ meta_core_show_window_menu (Display *xdisplay, | ||||
|     meta_window_raise (window); | ||||
|   meta_window_focus (window, timestamp); | ||||
|  | ||||
|   meta_window_show_menu (window); | ||||
|   meta_window_show_menu (window, root_x, root_y, button, timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_core_get_menu_accelerator (MetaMenuOp           menu_op, | ||||
|                                 int                  workspace, | ||||
|                                 unsigned int        *keysym, | ||||
|                                 MetaVirtualModifier *modifiers) | ||||
| { | ||||
|   const char *name; | ||||
|  | ||||
|   name = NULL; | ||||
|    | ||||
|   switch (menu_op) | ||||
|     { | ||||
|     case META_MENU_OP_NONE: | ||||
|       /* No keybinding for this one */ | ||||
|       break; | ||||
|     case META_MENU_OP_DELETE: | ||||
|       name = "close"; | ||||
|       break; | ||||
|     case META_MENU_OP_MINIMIZE: | ||||
|       name = "minimize"; | ||||
|       break; | ||||
|     case META_MENU_OP_UNMAXIMIZE: | ||||
|       name = "unmaximize"; | ||||
|       break; | ||||
|     case META_MENU_OP_MAXIMIZE: | ||||
|       name = "maximize"; | ||||
|       break; | ||||
|     case META_MENU_OP_UNSHADE: | ||||
|     case META_MENU_OP_SHADE: | ||||
|       name = "toggle_shaded"; | ||||
|       break; | ||||
|     case META_MENU_OP_UNSTICK: | ||||
|     case META_MENU_OP_STICK: | ||||
|       name = "toggle-on-all-workspaces"; | ||||
|       break; | ||||
|     case META_MENU_OP_ABOVE: | ||||
|     case META_MENU_OP_UNABOVE: | ||||
|       name = "toggle-above"; | ||||
|       break; | ||||
|     case META_MENU_OP_WORKSPACES: | ||||
|       switch (workspace) | ||||
|         { | ||||
|         case 1: | ||||
|           name = "move-to-workspace-1"; | ||||
|           break; | ||||
|         case 2: | ||||
|           name = "move-to-workspace-2"; | ||||
|           break; | ||||
|         case 3: | ||||
|           name = "move-to-workspace-3"; | ||||
|           break;  | ||||
|         case 4: | ||||
|           name = "move-to-workspace-4"; | ||||
|           break;  | ||||
|         case 5: | ||||
|           name = "move-to-workspace-5"; | ||||
|           break;  | ||||
|         case 6: | ||||
|           name = "move-to-workspace-6"; | ||||
|           break;  | ||||
|         case 7: | ||||
|           name = "move-to-workspace-7"; | ||||
|           break;  | ||||
|         case 8: | ||||
|           name = "move-to-workspace-8"; | ||||
|           break;  | ||||
|         case 9: | ||||
|           name = "move-to-workspace-9"; | ||||
|           break;  | ||||
|         case 10: | ||||
|           name = "move-to-workspace-10"; | ||||
|           break; | ||||
|         case 11: | ||||
|           name = "move-to-workspace-11"; | ||||
|           break; | ||||
|         case 12: | ||||
|           name = "move-to-workspace-12"; | ||||
|           break; | ||||
|         } | ||||
|       break; | ||||
|     case META_MENU_OP_MOVE: | ||||
|       name = "begin-move"; | ||||
|       break; | ||||
|     case META_MENU_OP_RESIZE: | ||||
|       name = "begin-resize"; | ||||
|       break; | ||||
|     case META_MENU_OP_MOVE_LEFT: | ||||
|       name = "move-to-workspace-left"; | ||||
|       break; | ||||
|     case META_MENU_OP_MOVE_RIGHT: | ||||
|       name = "move-to-workspace-right"; | ||||
|       break; | ||||
|     case META_MENU_OP_MOVE_UP: | ||||
|       name = "move-to-workspace-up"; | ||||
|       break; | ||||
|     case META_MENU_OP_MOVE_DOWN: | ||||
|       name = "move-to-workspace-down"; | ||||
|       break; | ||||
|     case META_MENU_OP_RECOVER: | ||||
|       /* No keybinding for this one */ | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   if (name) | ||||
|     { | ||||
|       meta_prefs_get_window_binding (name, keysym, modifiers); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       *keysym = 0; | ||||
|       *modifiers = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| const char* | ||||
|   | ||||
| @@ -159,8 +159,15 @@ const char* meta_core_get_workspace_name_with_index (Display *xdisplay, | ||||
|  | ||||
| void meta_core_show_window_menu (Display *xdisplay, | ||||
|                                  Window   frame_xwindow, | ||||
|                                  int      root_x, | ||||
|                                  int      root_y, | ||||
|                                  int      button, | ||||
|                                  guint32  timestamp); | ||||
|  | ||||
| void meta_core_get_menu_accelerator (MetaMenuOp           menu_op, | ||||
|                                      int                  workspace, | ||||
|                                      unsigned int        *keysym, | ||||
|                                      MetaVirtualModifier *modifiers); | ||||
|  | ||||
| gboolean   meta_core_begin_grab_op (Display    *xdisplay, | ||||
|                                     Window      frame_xwindow, | ||||
|   | ||||
| @@ -263,6 +263,10 @@ struct _MetaDisplay | ||||
|   /* Managed by group.c */ | ||||
|   GHashTable *groups_by_leader; | ||||
|  | ||||
|   /* currently-active window menu if any */ | ||||
|   MetaWindowMenu *window_menu; | ||||
|   MetaWindow *window_with_menu; | ||||
|  | ||||
|   /* Managed by window-props.c */ | ||||
|   MetaWindowPropHooks *prop_hooks_table; | ||||
|   GHashTable *prop_hooks; | ||||
|   | ||||
| @@ -564,6 +564,9 @@ meta_display_open (void) | ||||
|   the_display->monitor_cache_invalidated = TRUE; | ||||
|  | ||||
|   the_display->groups_by_leader = NULL; | ||||
|  | ||||
|   the_display->window_with_menu = NULL; | ||||
|   the_display->window_menu = NULL; | ||||
|    | ||||
|   the_display->screens = NULL; | ||||
|   the_display->active_screen = NULL; | ||||
| @@ -1292,6 +1295,10 @@ meta_get_display (void) | ||||
|   return the_display; | ||||
| } | ||||
|  | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| static gboolean dump_events = TRUE; | ||||
| #endif | ||||
|  | ||||
| static gboolean | ||||
| grab_op_is_mouse_only (MetaGrabOp op) | ||||
| { | ||||
| @@ -1411,20 +1418,6 @@ meta_grab_op_is_moving (MetaGrabOp op) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| grab_op_should_block_mouse_events (MetaGrabOp op) | ||||
| { | ||||
|   switch (op) | ||||
|     { | ||||
|     case META_GRAB_OP_WAYLAND_CLIENT: | ||||
|     case META_GRAB_OP_COMPOSITOR: | ||||
|       return TRUE; | ||||
|  | ||||
|     default: | ||||
|       return FALSE; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_display_xserver_time_is_before: | ||||
|  * @display: a #MetaDisplay | ||||
| @@ -2061,7 +2054,7 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|   switch (event->type) | ||||
|     { | ||||
|     case CLUTTER_BUTTON_PRESS: | ||||
|       if (grab_op_should_block_mouse_events (display->grab_op)) | ||||
|       if (display->grab_op == META_GRAB_OP_COMPOSITOR) | ||||
|         break; | ||||
|  | ||||
|       display->overlay_key_only_pressed = FALSE; | ||||
| @@ -2203,7 +2196,11 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|             { | ||||
|               if (meta_prefs_get_raise_on_click ()) | ||||
|                 meta_window_raise (window); | ||||
|               meta_window_show_menu (window); | ||||
|               meta_window_show_menu (window, | ||||
|                                      event->button.x, | ||||
|                                      event->button.y, | ||||
|                                      event->button.button, | ||||
|                                      event->any.time); | ||||
|               bypass_clutter = TRUE; | ||||
|               bypass_wayland = TRUE; | ||||
|             } | ||||
| @@ -2229,7 +2226,7 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|         } | ||||
|       break; | ||||
|     case CLUTTER_BUTTON_RELEASE: | ||||
|       if (grab_op_should_block_mouse_events (display->grab_op)) | ||||
|       if (display->grab_op == META_GRAB_OP_COMPOSITOR) | ||||
|         break; | ||||
|  | ||||
|       display->overlay_key_only_pressed = FALSE; | ||||
| @@ -2243,7 +2240,7 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|         } | ||||
|       break; | ||||
|     case CLUTTER_MOTION: | ||||
|       if (grab_op_should_block_mouse_events (display->grab_op)) | ||||
|       if (display->grab_op == META_GRAB_OP_COMPOSITOR) | ||||
|         break; | ||||
|  | ||||
|       if (display->grab_window == window && | ||||
| @@ -2277,10 +2274,6 @@ meta_display_handle_event (MetaDisplay        *display, | ||||
|   if (display->grab_op == META_GRAB_OP_COMPOSITOR) | ||||
|     bypass_wayland = TRUE; | ||||
|  | ||||
|   /* If a Wayland client has a grab, don't pass that through to Clutter */ | ||||
|   if (display->grab_op == META_GRAB_OP_WAYLAND_CLIENT) | ||||
|     bypass_clutter = TRUE; | ||||
|  | ||||
|   if (compositor && !bypass_wayland) | ||||
|     { | ||||
|       if (meta_wayland_compositor_handle_event (compositor, event)) | ||||
| @@ -2981,8 +2974,9 @@ meta_display_handle_xevent (MetaDisplay *display, | ||||
|   MetaMonitorManager *monitor; | ||||
|   MetaScreen *screen; | ||||
|  | ||||
| #if 0 | ||||
|   meta_spew_event (display, event); | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
|   if (dump_events) | ||||
|     meta_spew_event (display, event); | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_STARTUP_NOTIFICATION | ||||
| @@ -3258,7 +3252,8 @@ event_get_time (MetaDisplay *display, | ||||
|     } | ||||
| } | ||||
|  | ||||
| G_GNUC_UNUSED const char* | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| const char* | ||||
| meta_event_detail_to_string (int d) | ||||
| { | ||||
|   const char *detail = "???"; | ||||
| @@ -3294,8 +3289,10 @@ meta_event_detail_to_string (int d) | ||||
|  | ||||
|   return detail; | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| G_GNUC_UNUSED const char* | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| const char* | ||||
| meta_event_mode_to_string (int m) | ||||
| { | ||||
|   const char *mode = "???"; | ||||
| @@ -3317,8 +3314,10 @@ meta_event_mode_to_string (int m) | ||||
|  | ||||
|   return mode; | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| G_GNUC_UNUSED static const char* | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| static const char* | ||||
| stack_mode_to_string (int mode) | ||||
| { | ||||
|   switch (mode) | ||||
| @@ -3337,9 +3336,11 @@ stack_mode_to_string (int mode) | ||||
|  | ||||
|   return "Unknown"; | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| #ifdef HAVE_XSYNC | ||||
| G_GNUC_UNUSED static gint64 | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| static gint64 | ||||
| sync_value_to_64 (const XSyncValue *value) | ||||
| { | ||||
|   gint64 v; | ||||
| @@ -3349,8 +3350,10 @@ sync_value_to_64 (const XSyncValue *value) | ||||
|    | ||||
|   return v; | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| G_GNUC_UNUSED static const char* | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| static const char* | ||||
| alarm_state_to_string (XSyncAlarmState state) | ||||
| { | ||||
|   switch (state) | ||||
| @@ -3365,9 +3368,12 @@ alarm_state_to_string (XSyncAlarmState state) | ||||
|       return "(unknown)"; | ||||
|     } | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| #endif /* HAVE_XSYNC */ | ||||
|  | ||||
| G_GNUC_UNUSED static void | ||||
| #ifdef WITH_VERBOSE_MODE | ||||
| static void | ||||
| meta_spew_xi2_event (MetaDisplay *display, | ||||
|                      XIEvent     *input_event, | ||||
|                      const char **name_p, | ||||
| @@ -3427,7 +3433,7 @@ meta_spew_xi2_event (MetaDisplay *display, | ||||
|   *extra_p = extra; | ||||
| } | ||||
|  | ||||
| G_GNUC_UNUSED static void | ||||
| static void | ||||
| meta_spew_core_event (MetaDisplay *display, | ||||
|                       XEvent      *event, | ||||
|                       const char **name_p, | ||||
| @@ -3651,7 +3657,7 @@ meta_spew_core_event (MetaDisplay *display, | ||||
|   *extra_p = extra; | ||||
| } | ||||
|  | ||||
| G_GNUC_UNUSED static void | ||||
| static void | ||||
| meta_spew_event (MetaDisplay *display, | ||||
|                  XEvent      *event) | ||||
| { | ||||
| @@ -3660,6 +3666,9 @@ meta_spew_event (MetaDisplay *display, | ||||
|   char *winname; | ||||
|   MetaScreen *screen; | ||||
|   XIEvent *input_event; | ||||
|  | ||||
|   if (!meta_is_verbose()) | ||||
|     return; | ||||
|    | ||||
|   /* filter overnumerous events */ | ||||
|   if (event->type == Expose || event->type == MotionNotify || | ||||
| @@ -3689,16 +3698,18 @@ meta_spew_event (MetaDisplay *display, | ||||
|   else | ||||
|     winname = g_strdup_printf ("0x%lx", event->xany.window); | ||||
|  | ||||
|   g_print ("%s on %s%s %s %sserial %lu\n", name, winname, | ||||
|            extra ? ":" : "", extra ? extra : "", | ||||
|            event->xany.send_event ? "SEND " : "", | ||||
|            event->xany.serial); | ||||
|   meta_topic (META_DEBUG_EVENTS, | ||||
|               "%s on %s%s %s %sserial %lu\n", name, winname, | ||||
|               extra ? ":" : "", extra ? extra : "", | ||||
|               event->xany.send_event ? "SEND " : "", | ||||
|               event->xany.serial); | ||||
|  | ||||
|   g_free (winname); | ||||
|  | ||||
|   if (extra) | ||||
|     g_free (extra); | ||||
| } | ||||
| #endif /* WITH_VERBOSE_MODE */ | ||||
|  | ||||
| MetaWindow* | ||||
| meta_display_lookup_x_window (MetaDisplay *display, | ||||
|   | ||||
| @@ -1975,7 +1975,9 @@ process_mouse_move_resize_grab (MetaDisplay     *display, | ||||
|        * moveresize now to get the position back to the original. | ||||
|        */ | ||||
|       if (window->shaken_loose || window->tile_mode == META_TILE_MAXIMIZED) | ||||
|         meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|         meta_window_maximize (window, | ||||
|                               META_MAXIMIZE_HORIZONTAL | | ||||
|                               META_MAXIMIZE_VERTICAL); | ||||
|       else if (window->tile_mode != META_TILE_NONE) | ||||
|         meta_window_tile (window); | ||||
|       else | ||||
| @@ -2036,7 +2038,9 @@ process_keyboard_move_grab (MetaDisplay     *display, | ||||
|        * now to get the position back to the original. | ||||
|        */ | ||||
|       if (window->shaken_loose) | ||||
|         meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|         meta_window_maximize (window, | ||||
|                               META_MAXIMIZE_HORIZONTAL | | ||||
|                               META_MAXIMIZE_VERTICAL); | ||||
|       else | ||||
|         meta_window_move_resize (display->grab_window, | ||||
|                                  TRUE, | ||||
| @@ -2782,7 +2786,20 @@ handle_activate_window_menu (MetaDisplay     *display, | ||||
|                              gpointer         dummy) | ||||
| { | ||||
|   if (display->focus_window) | ||||
|     meta_window_show_menu (display->focus_window); | ||||
|     { | ||||
|       int x, y; | ||||
|  | ||||
|       meta_window_get_position (display->focus_window, | ||||
|                                 &x, &y); | ||||
|  | ||||
|       if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) | ||||
|         x += display->focus_window->rect.width; | ||||
|  | ||||
|       meta_window_show_menu (display->focus_window, | ||||
|                              x, y, | ||||
|                              0, | ||||
|                              event->time); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -2884,9 +2901,11 @@ handle_toggle_tiled (MetaDisplay     *display, | ||||
|         : META_TILE_NONE; | ||||
|  | ||||
|       if (window->saved_maximize) | ||||
|         meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|         meta_window_maximize (window, META_MAXIMIZE_VERTICAL | | ||||
|                               META_MAXIMIZE_HORIZONTAL); | ||||
|       else | ||||
|         meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|         meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL | | ||||
|                                 META_MAXIMIZE_HORIZONTAL); | ||||
|     } | ||||
|   else if (meta_window_can_tile_side_by_side (window)) | ||||
|     { | ||||
| @@ -2912,9 +2931,13 @@ handle_toggle_maximized    (MetaDisplay     *display, | ||||
|                             gpointer         dummy) | ||||
| { | ||||
|   if (META_WINDOW_MAXIMIZED (window)) | ||||
|     meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_unmaximize (window, | ||||
|                             META_MAXIMIZE_HORIZONTAL | | ||||
|                             META_MAXIMIZE_VERTICAL); | ||||
|   else if (window->has_maximize_func) | ||||
|     meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_maximize (window, | ||||
|                           META_MAXIMIZE_HORIZONTAL | | ||||
|                           META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -2926,7 +2949,9 @@ handle_maximize           (MetaDisplay     *display, | ||||
|                            gpointer         dummy) | ||||
| { | ||||
|   if (window->has_maximize_func) | ||||
|     meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_maximize (window, | ||||
|                           META_MAXIMIZE_HORIZONTAL | | ||||
|                           META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -2938,7 +2963,9 @@ handle_unmaximize         (MetaDisplay     *display, | ||||
|                            gpointer         dummy) | ||||
| { | ||||
|   if (window->maximized_vertically || window->maximized_horizontally) | ||||
|     meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_unmaximize (window, | ||||
|                             META_MAXIMIZE_HORIZONTAL | | ||||
|                             META_MAXIMIZE_VERTICAL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -3889,7 +3916,6 @@ init_builtin_key_bindings (MetaDisplay *display) | ||||
|  | ||||
|   g_object_unref (common_keybindings); | ||||
|   g_object_unref (mutter_keybindings); | ||||
|   g_object_unref (mutter_wayland_keybindings); | ||||
| } | ||||
|  | ||||
| void | ||||
|   | ||||
| @@ -42,7 +42,7 @@ | ||||
|  */ | ||||
|  | ||||
| #define _GNU_SOURCE | ||||
| #define _XOPEN_SOURCE /* for putenv() and some signal-related functions */ | ||||
| #define _SVID_SOURCE /* for putenv() and some signal-related functions */ | ||||
|  | ||||
| #include <config.h> | ||||
| #include <meta/main.h> | ||||
| @@ -190,7 +190,6 @@ static gboolean  opt_replace_wm; | ||||
| static gboolean  opt_disable_sm; | ||||
| static gboolean  opt_sync; | ||||
| static gboolean  opt_wayland; | ||||
| static gboolean  opt_display_server; | ||||
|  | ||||
| static GOptionEntry meta_options[] = { | ||||
|   { | ||||
| @@ -234,11 +233,6 @@ static GOptionEntry meta_options[] = { | ||||
|     N_("Run as a wayland compositor"), | ||||
|     NULL | ||||
|   }, | ||||
|   { | ||||
|     "display-server", 0, 0, G_OPTION_ARG_NONE, | ||||
|     &opt_display_server, | ||||
|     N_("Run as a full display server, rather than nested") | ||||
|   }, | ||||
|   {NULL} | ||||
| }; | ||||
|  | ||||
| @@ -407,7 +401,8 @@ meta_init (void) | ||||
|   if (g_getenv ("MUTTER_DEBUG")) | ||||
|     meta_set_debugging (TRUE); | ||||
|  | ||||
|   if (opt_display_server) | ||||
|   /* We consider running from mutter-launch equivalent to running from bare metal. */ | ||||
|   if (getenv ("WESTON_LAUNCHER_SOCK")) | ||||
|     clutter_set_windowing_backend (CLUTTER_WINDOWING_EGL); | ||||
|  | ||||
|   meta_set_is_wayland_compositor (opt_wayland); | ||||
| @@ -502,32 +497,6 @@ meta_register_with_session (void) | ||||
|   g_free (opt_client_id); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_activate_session: | ||||
|  * | ||||
|  * Tells mutter to activate the session. When mutter is a | ||||
|  * Wayland compositor, this tells logind to switch over to | ||||
|  * the new session. | ||||
|  */ | ||||
| gboolean | ||||
| meta_activate_session (void) | ||||
| { | ||||
|   if (meta_is_wayland_compositor ()) | ||||
|     { | ||||
|       MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); | ||||
|       GError *error = NULL; | ||||
|  | ||||
|       if (!meta_wayland_compositor_activate_session (compositor, &error)) | ||||
|         { | ||||
|           g_warning ("Could not activate session: %s\n", error->message); | ||||
|           g_error_free (error); | ||||
|           return FALSE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_run: (skip) | ||||
|  * | ||||
|   | ||||
| @@ -43,7 +43,4 @@ void     meta_cursor_tracker_update_position (MetaCursorTracker *tracker, | ||||
| 					      int                new_x, | ||||
| 					      int                new_y); | ||||
| void     meta_cursor_tracker_paint           (MetaCursorTracker *tracker); | ||||
|  | ||||
| void     meta_cursor_tracker_force_update (MetaCursorTracker *tracker); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -39,7 +39,6 @@ | ||||
| #include <gbm.h> | ||||
|  | ||||
| #include <gdk/gdk.h> | ||||
| #include <gdk/gdkx.h> | ||||
|  | ||||
| #include <X11/cursorfont.h> | ||||
| #include <X11/extensions/Xfixes.h> | ||||
| @@ -570,9 +569,6 @@ make_wayland_cursor_tracker (MetaScreen *screen) | ||||
|  | ||||
|   compositor = meta_wayland_compositor_get_default (); | ||||
|   compositor->seat->cursor_tracker = self; | ||||
|   meta_cursor_tracker_update_position (self, | ||||
|                                        wl_fixed_to_int (compositor->seat->pointer.x), | ||||
|                                        wl_fixed_to_int (compositor->seat->pointer.y)); | ||||
|  | ||||
| #if defined(CLUTTER_WINDOWING_EGL) | ||||
|   if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL)) | ||||
| @@ -1078,13 +1074,12 @@ get_pointer_position_gdk (int         *x, | ||||
|   GdkScreen *gscreen; | ||||
|  | ||||
|   gmanager = gdk_display_get_device_manager (gdk_display_get_default ()); | ||||
|   gdevice = gdk_x11_device_manager_lookup (gmanager, META_VIRTUAL_CORE_POINTER_ID); | ||||
|   gdevice = gdk_device_manager_get_client_pointer (gmanager); | ||||
|  | ||||
|   gdk_device_get_position (gdevice, &gscreen, x, y); | ||||
|   if (mods) | ||||
|     gdk_device_get_state (gdevice, | ||||
|                           gdk_screen_get_root_window (gscreen), | ||||
|                           NULL, (GdkModifierType*)mods); | ||||
|   gdk_device_get_state (gdevice, | ||||
|                         gdk_screen_get_root_window (gscreen), | ||||
|                         NULL, (GdkModifierType*)mods); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1100,12 +1095,9 @@ get_pointer_position_clutter (int         *x, | ||||
|   cdevice = clutter_device_manager_get_core_device (cmanager, CLUTTER_POINTER_DEVICE); | ||||
|  | ||||
|   clutter_input_device_get_coords (cdevice, NULL, &point); | ||||
|   if (x) | ||||
|     *x = point.x; | ||||
|   if (y) | ||||
|     *y = point.y; | ||||
|   if (mods) | ||||
|     *mods = clutter_input_device_get_modifier_state (cdevice); | ||||
|   *x = point.x; | ||||
|   *y = point.y; | ||||
|   *mods = clutter_input_device_get_modifier_state (cdevice); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -1147,12 +1139,3 @@ meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker, | ||||
|                           tracker->screen->xroot); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_cursor_tracker_force_update (MetaCursorTracker *tracker) | ||||
| { | ||||
|   g_assert (meta_is_wayland_compositor ()); | ||||
|  | ||||
|   update_hw_cursor (tracker); | ||||
|   sync_cursor (tracker); | ||||
| } | ||||
|   | ||||
| @@ -2241,6 +2241,43 @@ meta_prefs_get_keybinding_action (const char *name) | ||||
|               : META_KEYBINDING_ACTION_NONE; | ||||
| } | ||||
|  | ||||
| /* This is used by the menu system to decide what key binding | ||||
|  * to display next to an option. We return the first non-disabled | ||||
|  * binding, if any. | ||||
|  */ | ||||
| void | ||||
| meta_prefs_get_window_binding (const char          *name, | ||||
|                                unsigned int        *keysym, | ||||
|                                MetaVirtualModifier *modifiers) | ||||
| { | ||||
|   MetaKeyPref *pref = g_hash_table_lookup (key_bindings, name); | ||||
|  | ||||
|   if (pref->per_window) | ||||
|     { | ||||
|       GSList *s = pref->combos; | ||||
|  | ||||
|       while (s) | ||||
|         { | ||||
|           MetaKeyCombo *c = s->data; | ||||
|  | ||||
|           if (c->keysym != 0 || c->modifiers != 0) | ||||
|             { | ||||
|               *keysym = c->keysym; | ||||
|               *modifiers = c->modifiers; | ||||
|               return; | ||||
|             } | ||||
|  | ||||
|           s = s->next; | ||||
|         } | ||||
|  | ||||
|       /* Not found; return the disabled value */ | ||||
|       *keysym = *modifiers = 0; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   g_assert_not_reached (); | ||||
| } | ||||
|  | ||||
| gint | ||||
| meta_prefs_get_mouse_button_resize (void) | ||||
| { | ||||
|   | ||||
| @@ -310,7 +310,6 @@ set_supported_hint (MetaScreen *screen) | ||||
| #undef EWMH_ATOMS_ONLY | ||||
|  | ||||
|     screen->display->atom__GTK_FRAME_EXTENTS, | ||||
|     screen->display->atom__GTK_SHOW_WINDOW_MENU, | ||||
|   }; | ||||
|  | ||||
|   XChangeProperty (screen->display->xdisplay, screen->xroot, | ||||
| @@ -1515,19 +1514,38 @@ meta_screen_get_mouse_window (MetaScreen  *screen, | ||||
|                               MetaWindow  *not_this_one) | ||||
| { | ||||
|   MetaWindow *window; | ||||
|   int x, y; | ||||
|   Window root_return, child_return; | ||||
|   double root_x_return, root_y_return; | ||||
|   double win_x_return, win_y_return; | ||||
|   XIButtonState buttons; | ||||
|   XIModifierState mods; | ||||
|   XIGroupState group; | ||||
|  | ||||
|   if (not_this_one) | ||||
|     meta_topic (META_DEBUG_FOCUS, | ||||
|                 "Focusing mouse window excluding %s\n", not_this_one->desc); | ||||
|  | ||||
|   meta_cursor_tracker_get_pointer (screen->cursor_tracker, | ||||
|                                    &x, &y, NULL); | ||||
|   meta_error_trap_push (screen->display); | ||||
|   XIQueryPointer (screen->display->xdisplay, | ||||
|                   META_VIRTUAL_CORE_POINTER_ID, | ||||
|                   screen->xroot, | ||||
|                   &root_return, | ||||
|                   &child_return, | ||||
|                   &root_x_return, | ||||
|                   &root_y_return, | ||||
|                   &win_x_return, | ||||
|                   &win_y_return, | ||||
|                   &buttons, | ||||
|                   &mods, | ||||
|                   &group); | ||||
|   meta_error_trap_pop (screen->display); | ||||
|   free (buttons.mask); | ||||
|  | ||||
|   window = meta_stack_get_default_focus_window_at_point (screen->stack, | ||||
|                                                          screen->active_workspace, | ||||
|                                                          not_this_one, | ||||
|                                                          x, y); | ||||
|                                                          root_x_return, | ||||
|                                                          root_y_return); | ||||
|  | ||||
|   return window; | ||||
| } | ||||
| @@ -1809,11 +1827,28 @@ meta_screen_get_current_monitor (MetaScreen *screen) | ||||
|    | ||||
|   if (screen->display->monitor_cache_invalidated) | ||||
|     { | ||||
|       int x, y; | ||||
|       Window root_return, child_return; | ||||
|       double win_x_return, win_y_return; | ||||
|       double root_x_return, root_y_return; | ||||
|       XIButtonState buttons; | ||||
|       XIModifierState mods; | ||||
|       XIGroupState group; | ||||
|  | ||||
|       meta_cursor_tracker_get_pointer (screen->cursor_tracker, | ||||
|                                        &x, &y, NULL); | ||||
|       meta_screen_get_current_monitor_for_pos (screen, x, y); | ||||
|       XIQueryPointer (screen->display->xdisplay, | ||||
|                       META_VIRTUAL_CORE_POINTER_ID, | ||||
|                       screen->xroot, | ||||
|                       &root_return, | ||||
|                       &child_return, | ||||
|                       &root_x_return, | ||||
|                       &root_y_return, | ||||
|                       &win_x_return, | ||||
|                       &win_y_return, | ||||
|                       &buttons, | ||||
|                       &mods, | ||||
|                       &group); | ||||
|       free (buttons.mask); | ||||
|  | ||||
|       meta_screen_get_current_monitor_for_pos (screen, root_x_return, root_y_return); | ||||
|     } | ||||
|  | ||||
|   return screen->last_monitor_index; | ||||
|   | ||||
| @@ -408,12 +408,6 @@ struct _MetaWindow | ||||
|    */ | ||||
|   MetaRectangle rect; | ||||
|  | ||||
|   /* The size and position we want the window to be (i.e. what we last asked | ||||
|    * the client to configure). | ||||
|    * This is only used for wayland clients. | ||||
|    */ | ||||
|   MetaRectangle expected_rect; | ||||
|  | ||||
|   gboolean has_custom_frame_extents; | ||||
|   GtkBorder custom_frame_extents; | ||||
|  | ||||
| @@ -628,7 +622,11 @@ void     meta_window_set_current_workspace_hint (MetaWindow *window); | ||||
|  | ||||
| unsigned long meta_window_get_net_wm_desktop (MetaWindow *window); | ||||
|  | ||||
| void meta_window_show_menu (MetaWindow *window); | ||||
| void meta_window_show_menu (MetaWindow *window, | ||||
|                             int         root_x, | ||||
|                             int         root_y, | ||||
|                             int         button, | ||||
|                             guint32     timestamp); | ||||
|  | ||||
| gboolean meta_window_titlebar_is_onscreen    (MetaWindow *window); | ||||
| void     meta_window_shove_titlebar_onscreen (MetaWindow *window); | ||||
| @@ -680,8 +678,9 @@ void meta_window_update_layer (MetaWindow *window); | ||||
|  | ||||
| void meta_window_recalc_features    (MetaWindow *window); | ||||
|  | ||||
| void meta_window_set_type (MetaWindow     *window, | ||||
|                            MetaWindowType  type); | ||||
| /* recalc_window_type is x11 only, wayland does its thing and then calls type_changed */ | ||||
| void meta_window_recalc_window_type (MetaWindow *window); | ||||
| void meta_window_type_changed       (MetaWindow *window); | ||||
|  | ||||
| void meta_window_frame_size_changed (MetaWindow *window); | ||||
|  | ||||
| @@ -746,6 +745,4 @@ void meta_window_activate_full (MetaWindow     *window, | ||||
|                                 MetaClientType  source_indication, | ||||
|                                 MetaWorkspace  *workspace); | ||||
|  | ||||
| gboolean meta_window_is_client_decorated (MetaWindow *window); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|  */ | ||||
|  | ||||
| #define _GNU_SOURCE | ||||
| #define _XOPEN_SOURCE 500 /* for gethostname() */ | ||||
| #define _SVID_SOURCE /* for gethostname() */ | ||||
|  | ||||
| #include <config.h> | ||||
| #include "window-props.h" | ||||
| @@ -727,7 +727,7 @@ reload_net_wm_state (MetaWindow    *window, | ||||
|   meta_verbose ("Reloaded _NET_WM_STATE for %s\n", | ||||
|                 window->desc); | ||||
|  | ||||
|   meta_window_x11_recalc_window_type (window); | ||||
|   meta_window_recalc_window_type (window); | ||||
|   meta_window_recalc_features (window); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,6 @@ | ||||
| #include <meta/common.h> | ||||
| #include <meta/errors.h> | ||||
| #include <meta/prefs.h> | ||||
| #include <meta/meta-cursor-tracker.h> | ||||
|  | ||||
| #include "window-private.h" | ||||
| #include "window-props.h" | ||||
| @@ -226,7 +225,7 @@ meta_window_x11_update_net_wm_type (MetaWindow *window) | ||||
|         meta_XFree (str); | ||||
|     } | ||||
|  | ||||
|   meta_window_x11_recalc_window_type (window); | ||||
|   meta_window_recalc_window_type (window); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -674,19 +673,35 @@ meta_window_x11_property_notify (MetaWindow *window, | ||||
| static int | ||||
| query_pressed_buttons (MetaWindow *window) | ||||
| { | ||||
|   ClutterModifierType mods; | ||||
|   double x, y, query_root_x, query_root_y; | ||||
|   Window root, child; | ||||
|   XIButtonState buttons; | ||||
|   XIModifierState mods; | ||||
|   XIGroupState group; | ||||
|   int button = 0; | ||||
|  | ||||
|   meta_cursor_tracker_get_pointer (window->screen->cursor_tracker, | ||||
|                                    NULL, NULL, &mods); | ||||
|   meta_error_trap_push (window->display); | ||||
|   XIQueryPointer (window->display->xdisplay, | ||||
|                   META_VIRTUAL_CORE_POINTER_ID, | ||||
|                   window->xwindow, | ||||
|                   &root, &child, | ||||
|                   &query_root_x, &query_root_y, | ||||
|                   &x, &y, | ||||
|                   &buttons, &mods, &group); | ||||
|  | ||||
|   if (mods & CLUTTER_BUTTON1_MASK) | ||||
|   if (meta_error_trap_pop_with_return (window->display) != Success) | ||||
|     goto out; | ||||
|  | ||||
|   if (XIMaskIsSet (buttons.mask, Button1)) | ||||
|     button |= 1 << 1; | ||||
|   if (mods & CLUTTER_BUTTON2_MASK) | ||||
|   if (XIMaskIsSet (buttons.mask, Button2)) | ||||
|     button |= 1 << 2; | ||||
|   if (mods & CLUTTER_BUTTON3_MASK) | ||||
|   if (XIMaskIsSet (buttons.mask, Button3)) | ||||
|     button |= 1 << 3; | ||||
|  | ||||
|   free (buttons.mask); | ||||
|  | ||||
|  out: | ||||
|   return button; | ||||
| } | ||||
|  | ||||
| @@ -872,7 +887,7 @@ meta_window_x11_client_message (MetaWindow *window, | ||||
|             (action == _NET_WM_STATE_ADD) || | ||||
|             (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal); | ||||
|  | ||||
|           meta_window_x11_recalc_window_type (window); | ||||
|           meta_window_recalc_window_type (window); | ||||
|           meta_window_queue(window, META_QUEUE_MOVE_RESIZE); | ||||
|         } | ||||
|  | ||||
| @@ -1165,11 +1180,6 @@ meta_window_x11_client_message (MetaWindow *window, | ||||
|  | ||||
|       meta_window_update_fullscreen_monitors (window, top, bottom, left, right); | ||||
|     } | ||||
|   else if (event->xclient.message_type == | ||||
|            display->atom__GTK_SHOW_WINDOW_MENU) | ||||
|     { | ||||
|       meta_window_show_menu (window); | ||||
|     } | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
| @@ -1511,114 +1521,3 @@ error: | ||||
|   meta_error_trap_pop (display); | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_x11_recalc_window_type (MetaWindow *window) | ||||
| { | ||||
|   MetaWindowType type; | ||||
|  | ||||
|   if (window->type_atom != None) | ||||
|     { | ||||
|       if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP) | ||||
|         type = META_WINDOW_DESKTOP; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DOCK) | ||||
|         type = META_WINDOW_DOCK; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR) | ||||
|         type = META_WINDOW_TOOLBAR; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_MENU) | ||||
|         type = META_WINDOW_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY) | ||||
|         type = META_WINDOW_UTILITY; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH) | ||||
|         type = META_WINDOW_SPLASHSCREEN; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG) | ||||
|         type = META_WINDOW_DIALOG; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL) | ||||
|         type = META_WINDOW_NORMAL; | ||||
|       /* The below are *typically* override-redirect windows, but the spec does | ||||
|        * not disallow using them for managed windows. | ||||
|        */ | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) | ||||
|         type = META_WINDOW_DROPDOWN_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU) | ||||
|         type = META_WINDOW_POPUP_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP) | ||||
|         type = META_WINDOW_TOOLTIP; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION) | ||||
|         type = META_WINDOW_NOTIFICATION; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_COMBO) | ||||
|         type = META_WINDOW_COMBO; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DND) | ||||
|         type = META_WINDOW_DND; | ||||
|       else | ||||
|         { | ||||
|           char *atom_name; | ||||
|  | ||||
|           /* | ||||
|            * Fallback on a normal type, and print warning. Don't abort. | ||||
|            */ | ||||
|           type = META_WINDOW_NORMAL; | ||||
|  | ||||
|           meta_error_trap_push (window->display); | ||||
|           atom_name = XGetAtomName (window->display->xdisplay, | ||||
|                                     window->type_atom); | ||||
|           meta_error_trap_pop (window->display); | ||||
|  | ||||
|           meta_warning ("Unrecognized type atom [%s] set for %s \n", | ||||
|                         atom_name ? atom_name : "unknown", | ||||
|                         window->desc); | ||||
|  | ||||
|           if (atom_name) | ||||
|             XFree (atom_name); | ||||
|         } | ||||
|     } | ||||
|   else if (window->transient_for != NULL) | ||||
|     { | ||||
|       type = META_WINDOW_DIALOG; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       type = META_WINDOW_NORMAL; | ||||
|     } | ||||
|  | ||||
|   if (type == META_WINDOW_DIALOG && | ||||
|       window->wm_state_modal) | ||||
|     type = META_WINDOW_MODAL_DIALOG; | ||||
|  | ||||
|   /* We don't want to allow override-redirect windows to have decorated-window | ||||
|    * types since that's just confusing. | ||||
|    */ | ||||
|   if (window->override_redirect) | ||||
|     { | ||||
|       switch (window->type) | ||||
|         { | ||||
|         /* Decorated types */ | ||||
|         case META_WINDOW_NORMAL: | ||||
|         case META_WINDOW_DIALOG: | ||||
|         case META_WINDOW_MODAL_DIALOG: | ||||
|         case META_WINDOW_MENU: | ||||
|         case META_WINDOW_UTILITY: | ||||
|           type = META_WINDOW_OVERRIDE_OTHER; | ||||
|           break; | ||||
|         /* Undecorated types, normally not override-redirect */ | ||||
|         case META_WINDOW_DESKTOP: | ||||
|         case META_WINDOW_DOCK: | ||||
|         case META_WINDOW_TOOLBAR: | ||||
|         case META_WINDOW_SPLASHSCREEN: | ||||
|         /* Undecorated types, normally override-redirect types */ | ||||
|         case META_WINDOW_DROPDOWN_MENU: | ||||
|         case META_WINDOW_POPUP_MENU: | ||||
|         case META_WINDOW_TOOLTIP: | ||||
|         case META_WINDOW_NOTIFICATION: | ||||
|         case META_WINDOW_COMBO: | ||||
|         case META_WINDOW_DND: | ||||
|         /* To complete enum */ | ||||
|         case META_WINDOW_OVERRIDE_OTHER: | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   meta_verbose ("Calculated type %u for %s, old type %u\n", | ||||
|                 type, window->desc, type); | ||||
|   meta_window_set_type (window, type); | ||||
| } | ||||
|   | ||||
| @@ -35,8 +35,6 @@ void meta_window_x11_update_opaque_region        (MetaWindow *window); | ||||
| void meta_window_x11_update_input_region         (MetaWindow *window); | ||||
| void meta_window_x11_update_shape_region         (MetaWindow *window); | ||||
|  | ||||
| void meta_window_x11_recalc_window_type          (MetaWindow *window); | ||||
|  | ||||
| gboolean meta_window_x11_configure_request       (MetaWindow *window, | ||||
|                                                   XEvent     *event); | ||||
| gboolean meta_window_x11_property_notify         (MetaWindow *window, | ||||
|   | ||||
| @@ -1368,7 +1368,9 @@ meta_window_apply_session_info (MetaWindow *window, | ||||
|  | ||||
|       if (window->has_maximize_func && info->maximized) | ||||
|         { | ||||
|           meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|           meta_window_maximize (window, | ||||
|                                 META_MAXIMIZE_HORIZONTAL | | ||||
|                                 META_MAXIMIZE_VERTICAL); | ||||
|  | ||||
|           if (info->saved_rect_set) | ||||
|             { | ||||
| @@ -1522,6 +1524,13 @@ meta_window_unmanage (MetaWindow  *window, | ||||
|       meta_compositor_remove_window (window->display->compositor, window); | ||||
|     } | ||||
|  | ||||
|   if (window->display->window_with_menu == window) | ||||
|     { | ||||
|       meta_ui_window_menu_free (window->display->window_menu); | ||||
|       window->display->window_menu = NULL; | ||||
|       window->display->window_with_menu = NULL; | ||||
|     } | ||||
|  | ||||
|   if (destroying_windows_disallowed > 0) | ||||
|     meta_bug ("Tried to destroy window %s while destruction was not allowed\n", | ||||
|               window->desc); | ||||
| @@ -3111,9 +3120,6 @@ meta_window_maximize_internal (MetaWindow        *window, | ||||
|   meta_window_recalc_features (window); | ||||
|   set_net_wm_state (window); | ||||
|  | ||||
|   if (window->surface && window->maximized_horizontally && window->maximized_vertically) | ||||
|     meta_wayland_surface_send_maximized (window->surface); | ||||
|  | ||||
|   g_object_freeze_notify (G_OBJECT (window)); | ||||
|   g_object_notify (G_OBJECT (window), "maximized-horizontally"); | ||||
|   g_object_notify (G_OBJECT (window), "maximized-vertically"); | ||||
| @@ -3361,7 +3367,7 @@ meta_window_tile (MetaWindow *window) | ||||
|     return; | ||||
|  | ||||
|   if (window->tile_mode == META_TILE_MAXIMIZED) | ||||
|     directions = META_MAXIMIZE_BOTH; | ||||
|     directions = META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL; | ||||
|   else | ||||
|     directions = META_MAXIMIZE_VERTICAL; | ||||
|  | ||||
| @@ -3599,13 +3605,10 @@ meta_window_unmaximize_internal (MetaWindow        *window, | ||||
|       set_net_wm_state (window); | ||||
|     } | ||||
|  | ||||
|   if (window->surface && !window->maximized_horizontally && !window->maximized_vertically) | ||||
|     meta_wayland_surface_send_unmaximized (window->surface); | ||||
|  | ||||
|   g_object_freeze_notify (G_OBJECT (window)); | ||||
|   g_object_notify (G_OBJECT (window), "maximized-horizontally"); | ||||
|   g_object_notify (G_OBJECT (window), "maximized-vertically"); | ||||
|   g_object_thaw_notify (G_OBJECT (window)); | ||||
|     g_object_freeze_notify (G_OBJECT (window)); | ||||
|     g_object_notify (G_OBJECT (window), "maximized-horizontally"); | ||||
|     g_object_notify (G_OBJECT (window), "maximized-vertically"); | ||||
|     g_object_thaw_notify (G_OBJECT (window)); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -3708,9 +3711,6 @@ meta_window_make_fullscreen_internal (MetaWindow  *window) | ||||
|       /* For the auto-minimize feature, if we fail to get focus */ | ||||
|       meta_screen_queue_check_fullscreen (window->screen); | ||||
|  | ||||
|       if (window->surface) | ||||
|         meta_wayland_surface_send_fullscreened (window->surface); | ||||
|  | ||||
|       g_object_notify (G_OBJECT (window), "fullscreen"); | ||||
|     } | ||||
| } | ||||
| @@ -3767,9 +3767,6 @@ meta_window_unmake_fullscreen (MetaWindow  *window) | ||||
|  | ||||
|       meta_window_update_layer (window); | ||||
|  | ||||
|       if (window->surface) | ||||
|         meta_wayland_surface_send_unfullscreened (window->surface); | ||||
|  | ||||
|       g_object_notify (G_OBJECT (window), "fullscreen"); | ||||
|     } | ||||
| } | ||||
| @@ -4599,70 +4596,34 @@ meta_window_move_resize_internal (MetaWindow          *window, | ||||
|       root_x_nw = new_rect.x; | ||||
|       root_y_nw = new_rect.y; | ||||
|  | ||||
|       /* First, save where we would like the client to be. This is used by the next | ||||
|        * attach to determine if the client is really moving/resizing or not. | ||||
|        */ | ||||
|       window->expected_rect = new_rect; | ||||
|  | ||||
|       if (is_wayland_resize) | ||||
|         { | ||||
|           /* This is a call to wl_surface_commit(), ignore the new_rect and | ||||
|            * update the real client size to match the buffer size. | ||||
|            */ | ||||
|  | ||||
|           window->rect.width = w; | ||||
|           window->rect.height = h; | ||||
|         } | ||||
|  | ||||
|       if (new_rect.width != window->rect.width || | ||||
|           new_rect.height != window->rect.height) | ||||
|         { | ||||
|           /* We need to resize the client. Resizing is in two parts: | ||||
|            * some of the movement happens immediately, and some happens as part | ||||
|            * of the resizing (through dx/dy in wl_surface_attach). | ||||
|            * | ||||
|            * To do so, we need to compute the resize from the point of the view | ||||
|            * of the client, and then adjust the immediate resize to match. | ||||
|            * | ||||
|            * dx/dy are the values we expect from the new attach(), while deltax/ | ||||
|            * deltay reflect the overall movement. | ||||
|            */ | ||||
|           MetaRectangle client_rect; | ||||
|           int dx, dy; | ||||
|           int deltax, deltay; | ||||
|           if (!is_wayland_resize) | ||||
|             meta_wayland_surface_configure_notify (window->surface, | ||||
|                                                    new_rect.width, | ||||
|                                                    new_rect.height); | ||||
|  | ||||
|           meta_rectangle_resize_with_gravity (&old_rect, | ||||
|                                               &client_rect, | ||||
|                                               &new_rect, | ||||
|                                               gravity, | ||||
|                                               new_rect.width, | ||||
|                                               new_rect.height); | ||||
|  | ||||
|           deltax = new_rect.x - old_rect.x; | ||||
|           deltay = new_rect.y - old_rect.y; | ||||
|           dx = client_rect.x - old_rect.x; | ||||
|           dy = client_rect.y - old_rect.y; | ||||
|           if (window->rect.width != new_rect.width || | ||||
|               window->rect.height != new_rect.height) | ||||
|             need_resize_client = TRUE; | ||||
|  | ||||
|           if (deltax != dx || deltay != dy) | ||||
|             need_move_client = TRUE; | ||||
|  | ||||
|           window->rect.x += (deltax - dx); | ||||
|           window->rect.y += (deltay - dy); | ||||
|  | ||||
|           need_resize_client = TRUE; | ||||
|           meta_wayland_surface_configure_notify (window->surface, | ||||
|                                                  new_rect.width, | ||||
|                                                  new_rect.height); | ||||
|           window->rect.width = new_rect.width; | ||||
|           window->rect.height = new_rect.height; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           /* No resize happening, we can just move the window and live with it. */ | ||||
|           if (window->rect.x != new_rect.x || | ||||
|               window->rect.y != new_rect.y) | ||||
|             need_move_client = TRUE; | ||||
|  | ||||
|           window->rect.x = new_rect.x; | ||||
|           window->rect.y = new_rect.y; | ||||
|         } | ||||
|       if (window->rect.x != new_rect.x || | ||||
|           window->rect.y != new_rect.y) | ||||
|         need_move_client = TRUE; | ||||
|  | ||||
|       window->rect.x = new_rect.x; | ||||
|       window->rect.y = new_rect.y; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| @@ -5074,18 +5035,16 @@ meta_window_move_resize_wayland (MetaWindow *window, | ||||
|  | ||||
|   flags = META_IS_WAYLAND_RESIZE; | ||||
|  | ||||
|   meta_window_get_position (window, &x, &y); | ||||
|   x += dx; y += dy; | ||||
|   x = window->rect.x + dx; | ||||
|   y = window->rect.y + dy; | ||||
|  | ||||
|   if (x != window->expected_rect.x || y != window->expected_rect.y) | ||||
|   if (dx != 0 || dy != 0) | ||||
|     flags |= META_IS_MOVE_ACTION; | ||||
|   if (width != window->expected_rect.width || | ||||
|       height != window->expected_rect.height) | ||||
|   if (width != window->rect.width || height != window->rect.height) | ||||
|     flags |= META_IS_RESIZE_ACTION; | ||||
|  | ||||
|   meta_window_move_resize_internal (window, flags, NorthWestGravity, | ||||
|                                     x, y, width, height); | ||||
|   save_user_window_placement (window); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -7055,7 +7014,122 @@ meta_window_update_struts (MetaWindow *window) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| void | ||||
| meta_window_recalc_window_type (MetaWindow *window) | ||||
| { | ||||
|   MetaWindowType old_type; | ||||
|  | ||||
|   old_type = window->type; | ||||
|  | ||||
|   if (window->type_atom != None) | ||||
|     { | ||||
|       if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP) | ||||
|         window->type = META_WINDOW_DESKTOP; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DOCK) | ||||
|         window->type = META_WINDOW_DOCK; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR) | ||||
|         window->type = META_WINDOW_TOOLBAR; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_MENU) | ||||
|         window->type = META_WINDOW_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY) | ||||
|         window->type = META_WINDOW_UTILITY; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH) | ||||
|         window->type = META_WINDOW_SPLASHSCREEN; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG) | ||||
|         window->type = META_WINDOW_DIALOG; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NORMAL) | ||||
|         window->type = META_WINDOW_NORMAL; | ||||
|       /* The below are *typically* override-redirect windows, but the spec does | ||||
|        * not disallow using them for managed windows. | ||||
|        */ | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) | ||||
|         window->type = META_WINDOW_DROPDOWN_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU) | ||||
|         window->type = META_WINDOW_POPUP_MENU; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP) | ||||
|         window->type = META_WINDOW_TOOLTIP; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION) | ||||
|         window->type = META_WINDOW_NOTIFICATION; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_COMBO) | ||||
|         window->type = META_WINDOW_COMBO; | ||||
|       else if (window->type_atom  == window->display->atom__NET_WM_WINDOW_TYPE_DND) | ||||
|         window->type = META_WINDOW_DND; | ||||
|       else | ||||
|         { | ||||
|           char *atom_name; | ||||
|  | ||||
|           /* | ||||
|            * Fallback on a normal type, and print warning. Don't abort. | ||||
|            */ | ||||
|           window->type = META_WINDOW_NORMAL; | ||||
|  | ||||
|           meta_error_trap_push (window->display); | ||||
|           atom_name = XGetAtomName (window->display->xdisplay, | ||||
|                                     window->type_atom); | ||||
|           meta_error_trap_pop (window->display); | ||||
|  | ||||
|           meta_warning ("Unrecognized type atom [%s] set for %s \n", | ||||
|                         atom_name ? atom_name : "unknown", | ||||
|                         window->desc); | ||||
|  | ||||
|           if (atom_name) | ||||
|             XFree (atom_name); | ||||
|         } | ||||
|     } | ||||
|   else if (window->transient_for != NULL) | ||||
|     { | ||||
|       window->type = META_WINDOW_DIALOG; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       window->type = META_WINDOW_NORMAL; | ||||
|     } | ||||
|  | ||||
|   if (window->type == META_WINDOW_DIALOG && | ||||
|       window->wm_state_modal) | ||||
|     window->type = META_WINDOW_MODAL_DIALOG; | ||||
|  | ||||
|   /* We don't want to allow override-redirect windows to have decorated-window | ||||
|    * types since that's just confusing. | ||||
|    */ | ||||
|   if (window->override_redirect) | ||||
|     { | ||||
|       switch (window->type) | ||||
|         { | ||||
|         /* Decorated types */ | ||||
|         case META_WINDOW_NORMAL: | ||||
|         case META_WINDOW_DIALOG: | ||||
|         case META_WINDOW_MODAL_DIALOG: | ||||
|         case META_WINDOW_MENU: | ||||
|         case META_WINDOW_UTILITY: | ||||
|           window->type = META_WINDOW_OVERRIDE_OTHER; | ||||
|           break; | ||||
|         /* Undecorated types, normally not override-redirect */ | ||||
|         case META_WINDOW_DESKTOP: | ||||
|         case META_WINDOW_DOCK: | ||||
|         case META_WINDOW_TOOLBAR: | ||||
|         case META_WINDOW_SPLASHSCREEN: | ||||
|         /* Undecorated types, normally override-redirect types */ | ||||
|         case META_WINDOW_DROPDOWN_MENU: | ||||
|         case META_WINDOW_POPUP_MENU: | ||||
|         case META_WINDOW_TOOLTIP: | ||||
|         case META_WINDOW_NOTIFICATION: | ||||
|         case META_WINDOW_COMBO: | ||||
|         case META_WINDOW_DND: | ||||
|         /* To complete enum */ | ||||
|         case META_WINDOW_OVERRIDE_OTHER: | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   meta_verbose ("Calculated type %u for %s, old type %u\n", | ||||
|                 window->type, window->desc, old_type); | ||||
|  | ||||
|   if (old_type != window->type) | ||||
|     meta_window_type_changed (window); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_type_changed (MetaWindow *window) | ||||
| { | ||||
|   gboolean old_decorated = window->decorated; | ||||
| @@ -7088,17 +7162,6 @@ meta_window_type_changed (MetaWindow *window) | ||||
|   g_object_thaw_notify (object); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_set_type (MetaWindow     *window, | ||||
|                       MetaWindowType  type) | ||||
| { | ||||
|   if (window->type == type) | ||||
|     return; | ||||
|  | ||||
|   window->type = type; | ||||
|   meta_window_type_changed (window); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_frame_size_changed (MetaWindow *window) | ||||
| { | ||||
| @@ -7411,11 +7474,272 @@ meta_window_recalc_features (MetaWindow *window) | ||||
|    */ | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_show_menu (MetaWindow *window) | ||||
| static void | ||||
| menu_callback (MetaWindowMenu *menu, | ||||
|                Display        *xdisplay, | ||||
|                Window          client_xwindow, | ||||
|                guint32         timestamp, | ||||
|                MetaMenuOp      op, | ||||
|                int             workspace_index, | ||||
|                gpointer        data) | ||||
| { | ||||
|   MetaDisplay *display; | ||||
|   MetaWindow *window; | ||||
|   MetaWorkspace *workspace; | ||||
|  | ||||
|   display = meta_display_for_x_display (xdisplay); | ||||
|   window = meta_display_lookup_x_window (display, client_xwindow); | ||||
|   workspace = NULL; | ||||
|  | ||||
|   if (window != NULL) /* window can be NULL */ | ||||
|     { | ||||
|       meta_verbose ("Menu op %u on %s\n", op, window->desc); | ||||
|  | ||||
|       switch (op) | ||||
|         { | ||||
|         case META_MENU_OP_NONE: | ||||
|           /* nothing */ | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_DELETE: | ||||
|           meta_window_delete (window, timestamp); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MINIMIZE: | ||||
|           meta_window_minimize (window); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_UNMAXIMIZE: | ||||
|           meta_window_unmaximize (window, | ||||
|                                   META_MAXIMIZE_HORIZONTAL | | ||||
|                                   META_MAXIMIZE_VERTICAL); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MAXIMIZE: | ||||
|           meta_window_maximize (window, | ||||
|                                 META_MAXIMIZE_HORIZONTAL | | ||||
|                                 META_MAXIMIZE_VERTICAL); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_UNSHADE: | ||||
|           meta_window_unshade (window, timestamp); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_SHADE: | ||||
|           meta_window_shade (window, timestamp); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MOVE_LEFT: | ||||
|           workspace = meta_workspace_get_neighbor (window->screen->active_workspace, | ||||
|                                                    META_MOTION_LEFT); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MOVE_RIGHT: | ||||
|           workspace = meta_workspace_get_neighbor (window->screen->active_workspace, | ||||
|                                                    META_MOTION_RIGHT); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MOVE_UP: | ||||
|           workspace = meta_workspace_get_neighbor (window->screen->active_workspace, | ||||
|                                                    META_MOTION_UP); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MOVE_DOWN: | ||||
|           workspace = meta_workspace_get_neighbor (window->screen->active_workspace, | ||||
|                                                    META_MOTION_DOWN); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_WORKSPACES: | ||||
|           workspace = meta_screen_get_workspace_by_index (window->screen, | ||||
|                                                           workspace_index); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_STICK: | ||||
|           meta_window_stick (window); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_UNSTICK: | ||||
|           meta_window_unstick (window); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_ABOVE: | ||||
|         case META_MENU_OP_UNABOVE: | ||||
|           if (window->wm_state_above == FALSE) | ||||
|             meta_window_make_above (window); | ||||
|           else | ||||
|             meta_window_unmake_above (window); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_MOVE: | ||||
|           meta_window_begin_grab_op (window, | ||||
|                                      META_GRAB_OP_KEYBOARD_MOVING, | ||||
|                                      TRUE, | ||||
|                                      timestamp); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_RESIZE: | ||||
|           meta_window_begin_grab_op (window, | ||||
|                                      META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN, | ||||
|                                      TRUE, | ||||
|                                      timestamp); | ||||
|           break; | ||||
|  | ||||
|         case META_MENU_OP_RECOVER: | ||||
|           meta_window_shove_titlebar_onscreen (window); | ||||
|           break; | ||||
|  | ||||
|         default: | ||||
|           meta_warning (G_STRLOC": Unknown window op\n"); | ||||
|           break; | ||||
|         } | ||||
|  | ||||
|       if (workspace) | ||||
| 	{ | ||||
| 	  meta_window_change_workspace (window, | ||||
| 					workspace); | ||||
| 	} | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       meta_verbose ("Menu callback on nonexistent window\n"); | ||||
|     } | ||||
|  | ||||
|   if (display->window_menu == menu) | ||||
|     { | ||||
|       display->window_menu = NULL; | ||||
|       display->window_with_menu = NULL; | ||||
|     } | ||||
|  | ||||
|   meta_ui_window_menu_free (menu); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_show_menu (MetaWindow *window, | ||||
|                        int         root_x, | ||||
|                        int         root_y, | ||||
|                        int         button, | ||||
|                        guint32     timestamp) | ||||
| { | ||||
|   MetaMenuOp ops; | ||||
|   MetaMenuOp insensitive; | ||||
|   MetaWindowMenu *menu; | ||||
|   MetaWorkspaceLayout layout; | ||||
|   int n_workspaces; | ||||
|   gboolean ltr; | ||||
|  | ||||
|   g_return_if_fail (!window->override_redirect); | ||||
|   meta_compositor_show_window_menu (window->display->compositor, window); | ||||
|  | ||||
|   if (window->display->window_menu) | ||||
|     { | ||||
|       meta_ui_window_menu_free (window->display->window_menu); | ||||
|       window->display->window_menu = NULL; | ||||
|       window->display->window_with_menu = NULL; | ||||
|     } | ||||
|  | ||||
|   ops = META_MENU_OP_NONE; | ||||
|   insensitive = META_MENU_OP_NONE; | ||||
|  | ||||
|   ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE); | ||||
|  | ||||
|   if (!meta_window_titlebar_is_onscreen (window) && | ||||
|       window->type != META_WINDOW_DOCK && | ||||
|       window->type != META_WINDOW_DESKTOP) | ||||
|     ops |= META_MENU_OP_RECOVER; | ||||
|  | ||||
|   if (!meta_prefs_get_workspaces_only_on_primary () || | ||||
|       meta_window_is_on_primary_monitor (window)) | ||||
|     { | ||||
|       n_workspaces = meta_screen_get_n_workspaces (window->screen); | ||||
|  | ||||
|       if (n_workspaces > 1) | ||||
|         ops |= META_MENU_OP_WORKSPACES; | ||||
|  | ||||
|       meta_screen_calc_workspace_layout (window->screen, | ||||
|                                          n_workspaces, | ||||
|                                          meta_workspace_index ( window->screen->active_workspace), | ||||
|                                          &layout); | ||||
|  | ||||
|       if (!window->on_all_workspaces) | ||||
|         { | ||||
|           ltr = meta_ui_get_direction() == META_UI_DIRECTION_LTR; | ||||
|  | ||||
|           if (layout.current_col > 0) | ||||
|             ops |= ltr ? META_MENU_OP_MOVE_LEFT : META_MENU_OP_MOVE_RIGHT; | ||||
|           if ((layout.current_col < layout.cols - 1) && | ||||
|               (layout.current_row * layout.cols + (layout.current_col + 1) < n_workspaces)) | ||||
|             ops |= ltr ? META_MENU_OP_MOVE_RIGHT : META_MENU_OP_MOVE_LEFT; | ||||
|           if (layout.current_row > 0) | ||||
|             ops |= META_MENU_OP_MOVE_UP; | ||||
|           if ((layout.current_row < layout.rows - 1) && | ||||
|               ((layout.current_row + 1) * layout.cols + layout.current_col < n_workspaces)) | ||||
|             ops |= META_MENU_OP_MOVE_DOWN; | ||||
|         } | ||||
|  | ||||
|       meta_screen_free_workspace_layout (&layout); | ||||
|  | ||||
|       ops |= META_MENU_OP_UNSTICK; | ||||
|       ops |= META_MENU_OP_STICK; | ||||
|     } | ||||
|  | ||||
|   if (META_WINDOW_MAXIMIZED (window)) | ||||
|     ops |= META_MENU_OP_UNMAXIMIZE; | ||||
|   else | ||||
|     ops |= META_MENU_OP_MAXIMIZE; | ||||
|  | ||||
|   if (window->wm_state_above) | ||||
|     ops |= META_MENU_OP_UNABOVE; | ||||
|   else | ||||
|     ops |= META_MENU_OP_ABOVE; | ||||
|  | ||||
|   if (!window->has_maximize_func) | ||||
|     insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE; | ||||
|  | ||||
|   if (!window->has_minimize_func) | ||||
|     insensitive |= META_MENU_OP_MINIMIZE; | ||||
|  | ||||
|   if (!window->has_close_func) | ||||
|     insensitive |= META_MENU_OP_DELETE; | ||||
|  | ||||
|   if (!window->has_shade_func) | ||||
|     insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE; | ||||
|  | ||||
|   if (!META_WINDOW_ALLOWS_MOVE (window)) | ||||
|     insensitive |= META_MENU_OP_MOVE; | ||||
|  | ||||
|   if (!META_WINDOW_ALLOWS_RESIZE (window)) | ||||
|     insensitive |= META_MENU_OP_RESIZE; | ||||
|  | ||||
|    if (window->always_sticky) | ||||
|      insensitive |= META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES; | ||||
|  | ||||
|   if ((window->type == META_WINDOW_DESKTOP) || | ||||
|       (window->type == META_WINDOW_DOCK) || | ||||
|       (window->type == META_WINDOW_SPLASHSCREEN || | ||||
|       META_WINDOW_MAXIMIZED (window))) | ||||
|     insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE; | ||||
|  | ||||
|   /* If all operations are disabled, just quit without showing the menu. | ||||
|    * This is the case, for example, with META_WINDOW_DESKTOP windows. | ||||
|    */ | ||||
|   if ((ops & ~insensitive) == 0) | ||||
|     return; | ||||
|  | ||||
|   menu = | ||||
|     meta_ui_window_menu_new (window->screen->ui, | ||||
|                              window->xwindow, | ||||
|                              ops, | ||||
|                              insensitive, | ||||
|                              meta_window_get_net_wm_desktop (window), | ||||
|                              meta_screen_get_n_workspaces (window->screen), | ||||
|                              menu_callback, | ||||
|                              NULL); | ||||
|  | ||||
|   window->display->window_menu = menu; | ||||
|   window->display->window_with_menu = window; | ||||
|  | ||||
|   meta_verbose ("Popping up window menu for %s\n", window->desc); | ||||
|  | ||||
|   meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -7479,7 +7803,7 @@ meta_window_titlebar_is_onscreen (MetaWindow *window) | ||||
|  | ||||
|   /* Titlebar can't be offscreen if there is no titlebar... */ | ||||
|   if (!window->frame) | ||||
|     return TRUE; | ||||
|     return FALSE; | ||||
|  | ||||
|   /* Get the rectangle corresponding to the titlebar */ | ||||
|   meta_window_get_frame_rect (window, &titlebar_rect); | ||||
| @@ -7711,7 +8035,10 @@ update_move (MetaWindow  *window, | ||||
|       display->grab_anchor_root_x = x; | ||||
|       display->grab_anchor_root_y = y; | ||||
|  | ||||
|       meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|       meta_window_unmaximize (window, | ||||
|                               META_MAXIMIZE_HORIZONTAL | | ||||
|                               META_MAXIMIZE_VERTICAL); | ||||
|  | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -7755,7 +8082,9 @@ update_move (MetaWindow  *window, | ||||
|                   window->user_rect.x = window->saved_rect.x; | ||||
|                   window->user_rect.y = window->saved_rect.y; | ||||
|  | ||||
|                   meta_window_unmaximize (window, META_MAXIMIZE_BOTH); | ||||
|                   meta_window_unmaximize (window, | ||||
|                                           META_MAXIMIZE_HORIZONTAL | | ||||
|                                           META_MAXIMIZE_VERTICAL); | ||||
|                 } | ||||
|  | ||||
|               display->grab_initial_window_pos = work_area; | ||||
| @@ -7763,7 +8092,10 @@ update_move (MetaWindow  *window, | ||||
|               display->grab_anchor_root_y = y; | ||||
|               window->shaken_loose = FALSE; | ||||
|  | ||||
|               meta_window_maximize (window, META_MAXIMIZE_BOTH); | ||||
|               meta_window_maximize (window, | ||||
|                                     META_MAXIMIZE_HORIZONTAL | | ||||
|                                     META_MAXIMIZE_VERTICAL); | ||||
|  | ||||
|               return; | ||||
|             } | ||||
|         } | ||||
| @@ -8486,34 +8818,6 @@ meta_window_same_application (MetaWindow *window, | ||||
|     group==other_group; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * meta_window_is_client_decorated: | ||||
|  * | ||||
|  * Check if if the window has decorations drawn by the client. | ||||
|  * (window->decorated refers only to whether we should add decorations) | ||||
|  */ | ||||
| gboolean | ||||
| meta_window_is_client_decorated (MetaWindow *window) | ||||
| { | ||||
|   if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) | ||||
|     { | ||||
|       /* Assume all Wayland clients draw decorations - not strictly | ||||
|        * true but good enough for current purposes. | ||||
|        */ | ||||
|       return TRUE; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* Currently the implementation here is hackish - | ||||
|        * has_custom_frame_extents() is set if _GTK_FRAME_EXTENTS is set | ||||
|        * to any value even 0. GTK+ always sets _GTK_FRAME_EXTENTS for | ||||
|        * client-side-decorated window, even if the value is 0 because | ||||
|        * the window is maxized and has no invisible borders or shadows. | ||||
|        */ | ||||
|       return window->has_custom_frame_extents; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_refresh_resize_popup (MetaWindow *window) | ||||
| { | ||||
| @@ -9787,6 +10091,12 @@ meta_window_compute_tile_match (MetaWindow *window) | ||||
|     } | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_close (MetaWindow *window) | ||||
| { | ||||
|   return window->has_close_func; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_set_title (MetaWindow *window, | ||||
|                        const char *title) | ||||
| @@ -9870,8 +10180,7 @@ meta_window_set_transient_for (MetaWindow *window, | ||||
|     meta_window_propagate_focus_appearance (window, FALSE); | ||||
|  | ||||
|   /* may now be a dialog */ | ||||
|   if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) | ||||
|     meta_window_x11_recalc_window_type (window); | ||||
|   meta_window_recalc_window_type (window); | ||||
|  | ||||
|   if (!window->constructing) | ||||
|     { | ||||
| @@ -9997,15 +10306,25 @@ window_focus_on_pointer_rest_callback (gpointer data) | ||||
|   MetaWindow *window = focus_data->window; | ||||
|   MetaDisplay *display = window->display; | ||||
|   MetaScreen *screen = window->screen; | ||||
|   int root_x, root_y; | ||||
|   Window root, child; | ||||
|   double root_x, root_y, x, y; | ||||
|   guint32 timestamp; | ||||
|   ClutterActor *child; | ||||
|   XIButtonState buttons; | ||||
|   XIModifierState mods; | ||||
|   XIGroupState group; | ||||
|  | ||||
|   if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) | ||||
|     goto out; | ||||
|  | ||||
|   meta_cursor_tracker_get_pointer (screen->cursor_tracker, | ||||
|                                    &root_x, &root_y, NULL); | ||||
|   meta_error_trap_push (display); | ||||
|   XIQueryPointer (display->xdisplay, | ||||
|                   META_VIRTUAL_CORE_POINTER_ID, | ||||
|                   screen->xroot, | ||||
|                   &root, &child, | ||||
|                   &root_x, &root_y, &x, &y, | ||||
|                   &buttons, &mods, &group); | ||||
|   meta_error_trap_pop (display); | ||||
|   free (buttons.mask); | ||||
|  | ||||
|   if (root_x != focus_data->pointer_x || | ||||
|       root_y != focus_data->pointer_y) | ||||
| @@ -10015,15 +10334,17 @@ window_focus_on_pointer_rest_callback (gpointer data) | ||||
|       return TRUE; | ||||
|     } | ||||
|  | ||||
|   child = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (clutter_stage_get_default ()), | ||||
|                                           CLUTTER_PICK_REACTIVE, root_x, root_y); | ||||
|   if (!META_IS_SURFACE_ACTOR (child)) | ||||
|   /* Explicitly check for the overlay window, as get_focus_window_at_point() | ||||
|    * may return windows that extend underneath the chrome (like | ||||
|    * override-redirect or DESKTOP windows) | ||||
|    */ | ||||
|   if (child == meta_get_overlay_window (screen)) | ||||
|     goto out; | ||||
|  | ||||
|   window = | ||||
|     meta_stack_get_default_focus_window_at_point (screen->stack, | ||||
|                                                   screen->active_workspace, | ||||
|                                                   NULL, root_x, root_y); | ||||
|                                                   None, root_x, root_y); | ||||
|  | ||||
|   if (window == NULL) | ||||
|     goto out; | ||||
| @@ -10128,51 +10449,3 @@ meta_window_set_custom_frame_extents (MetaWindow *window, | ||||
|  | ||||
|   meta_window_queue (window, META_QUEUE_MOVE_RESIZE); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_maximize (MetaWindow *window) | ||||
| { | ||||
|   return window->has_maximize_func; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_minimize (MetaWindow *window) | ||||
| { | ||||
|   return window->has_minimize_func; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_shade (MetaWindow *window) | ||||
| { | ||||
|   return window->has_shade_func; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_can_close (MetaWindow *window) | ||||
| { | ||||
|   return window->has_close_func; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_is_always_on_all_workspaces (MetaWindow *window) | ||||
| { | ||||
|   return window->always_sticky; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_is_above (MetaWindow *window) | ||||
| { | ||||
|   return window->wm_state_above; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_allows_move (MetaWindow *window) | ||||
| { | ||||
|   return META_WINDOW_ALLOWS_MOVE (window); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_window_allows_resize (MetaWindow *window) | ||||
| { | ||||
|   return META_WINDOW_ALLOWS_RESIZE (window); | ||||
| } | ||||
|   | ||||
| @@ -62,7 +62,6 @@ item(_GTK_WINDOW_OBJECT_PATH) | ||||
| item(_GTK_APP_MENU_OBJECT_PATH) | ||||
| item(_GTK_MENUBAR_OBJECT_PATH) | ||||
| item(_GTK_FRAME_EXTENTS) | ||||
| item(_GTK_SHOW_WINDOW_MENU) | ||||
| item(_GNOME_WM_KEYBINDINGS) | ||||
| item(_GNOME_PANEL_ACTION) | ||||
| item(_GNOME_PANEL_ACTION_MAIN_MENU) | ||||
|   | ||||
| @@ -86,6 +86,61 @@ typedef enum | ||||
|   META_FRAME_TILED_RIGHT              = 1 << 16 | ||||
| } MetaFrameFlags; | ||||
|  | ||||
| /** | ||||
|  * MetaMenuOp: | ||||
|  * @META_MENU_OP_NONE: No menu operation | ||||
|  * @META_MENU_OP_DELETE: Menu operation delete | ||||
|  * @META_MENU_OP_MINIMIZE: Menu operation minimize | ||||
|  * @META_MENU_OP_UNMAXIMIZE: Menu operation unmaximize | ||||
|  * @META_MENU_OP_MAXIMIZE: Menu operation maximize | ||||
|  * @META_MENU_OP_UNSHADE: Menu operation unshade | ||||
|  * @META_MENU_OP_SHADE: Menu operation shade | ||||
|  * @META_MENU_OP_UNSTICK: Menu operation unstick | ||||
|  * @META_MENU_OP_STICK: Menu operation stick | ||||
|  * @META_MENU_OP_WORKSPACES: Menu operation workspaces | ||||
|  * @META_MENU_OP_MOVE: Menu operation move | ||||
|  * @META_MENU_OP_RESIZE: Menu operation resize | ||||
|  * @META_MENU_OP_ABOVE: Menu operation above | ||||
|  * @META_MENU_OP_UNABOVE: Menu operation unabove | ||||
|  * @META_MENU_OP_MOVE_LEFT: Menu operation left | ||||
|  * @META_MENU_OP_MOVE_RIGHT: Menu operation right | ||||
|  * @META_MENU_OP_MOVE_UP: Menu operation up | ||||
|  * @META_MENU_OP_MOVE_DOWN: Menu operation down | ||||
|  * @META_MENU_OP_RECOVER: Menu operation recover | ||||
|  */ | ||||
| typedef enum | ||||
| { | ||||
|   META_MENU_OP_NONE        = 0, | ||||
|   META_MENU_OP_DELETE      = 1 << 0, | ||||
|   META_MENU_OP_MINIMIZE    = 1 << 1, | ||||
|   META_MENU_OP_UNMAXIMIZE  = 1 << 2, | ||||
|   META_MENU_OP_MAXIMIZE    = 1 << 3, | ||||
|   META_MENU_OP_UNSHADE     = 1 << 4, | ||||
|   META_MENU_OP_SHADE       = 1 << 5, | ||||
|   META_MENU_OP_UNSTICK     = 1 << 6, | ||||
|   META_MENU_OP_STICK       = 1 << 7, | ||||
|   META_MENU_OP_WORKSPACES  = 1 << 8, | ||||
|   META_MENU_OP_MOVE        = 1 << 9, | ||||
|   META_MENU_OP_RESIZE      = 1 << 10, | ||||
|   META_MENU_OP_ABOVE       = 1 << 11, | ||||
|   META_MENU_OP_UNABOVE     = 1 << 12, | ||||
|   META_MENU_OP_MOVE_LEFT   = 1 << 13, | ||||
|   META_MENU_OP_MOVE_RIGHT  = 1 << 14, | ||||
|   META_MENU_OP_MOVE_UP     = 1 << 15, | ||||
|   META_MENU_OP_MOVE_DOWN   = 1 << 16, | ||||
|   META_MENU_OP_RECOVER     = 1 << 17 | ||||
| } MetaMenuOp; | ||||
|  | ||||
| typedef struct _MetaWindowMenu MetaWindowMenu; | ||||
|  | ||||
| typedef void (* MetaWindowMenuFunc) (MetaWindowMenu *menu, | ||||
|                                      Display        *xdisplay, | ||||
|                                      Window          client_xwindow, | ||||
|                                      guint32         timestamp, | ||||
|                                      MetaMenuOp      op, | ||||
|                                      int             workspace, | ||||
|                                      gpointer        user_data); | ||||
|  | ||||
| /** | ||||
|  * MetaGrabOp: | ||||
|  * @META_GRAB_OP_NONE: None | ||||
| @@ -173,10 +228,7 @@ typedef enum | ||||
|   META_GRAB_OP_CLICKING_UNSTICK, | ||||
|  | ||||
|   /* Special grab op when the compositor asked for a grab */ | ||||
|   META_GRAB_OP_COMPOSITOR, | ||||
|  | ||||
|   /* For when a client takes a popup grab */ | ||||
|   META_GRAB_OP_WAYLAND_CLIENT, | ||||
|   META_GRAB_OP_COMPOSITOR | ||||
| } MetaGrabOp; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -130,7 +130,5 @@ void meta_compositor_show_tile_preview (MetaCompositor *compositor, | ||||
|                                         int             tile_monitor_number); | ||||
| void meta_compositor_hide_tile_preview (MetaCompositor *compositor, | ||||
|                                         MetaScreen     *screen); | ||||
| void meta_compositor_show_window_menu (MetaCompositor *compositor, | ||||
|                                        MetaWindow     *window); | ||||
|  | ||||
| #endif /* META_COMPOSITOR_H */ | ||||
|   | ||||
| @@ -28,7 +28,6 @@ GOptionContext *meta_get_option_context     (void); | ||||
| void            meta_init                   (void); | ||||
| int             meta_run                    (void); | ||||
| void            meta_register_with_session  (void); | ||||
| gboolean        meta_activate_session       (void); | ||||
| gboolean        meta_get_replace_current_wm (void);  /* Actually defined in util.c */ | ||||
|  | ||||
| void            meta_set_wm_name              (const char *wm_name); | ||||
|   | ||||
| @@ -164,9 +164,6 @@ struct _MetaPluginClass | ||||
|                              int              tile_monitor_number); | ||||
|   void (*hide_tile_preview) (MetaPlugin      *plugin); | ||||
|  | ||||
|   void (*show_window_menu)  (MetaPlugin      *plugin, | ||||
|                              MetaWindow      *window); | ||||
|  | ||||
|   /** | ||||
|    * MetaPluginClass::kill_window_effects: | ||||
|    * @actor: a #MetaWindowActor | ||||
|   | ||||
| @@ -392,6 +392,10 @@ GType meta_key_binding_get_type    (void); | ||||
|  | ||||
| MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name); | ||||
|  | ||||
| void meta_prefs_get_window_binding (const char          *name, | ||||
|                                     unsigned int        *keysym, | ||||
|                                     MetaVirtualModifier *modifiers); | ||||
|  | ||||
| gboolean           meta_prefs_get_visual_bell      (void); | ||||
| gboolean           meta_prefs_bell_is_audible      (void); | ||||
| GDesktopVisualBellType meta_prefs_get_visual_bell_type (void); | ||||
|   | ||||
| @@ -72,13 +72,11 @@ typedef enum | ||||
|  * MetaMaximizeFlags: | ||||
|  * @META_MAXIMIZE_HORIZONTAL: Horizontal | ||||
|  * @META_MAXIMIZE_VERTICAL: Vertical | ||||
|  * @META_MAXIMIZE_BOTH: Both | ||||
|  */ | ||||
| typedef enum | ||||
| { | ||||
|   META_MAXIMIZE_HORIZONTAL = 1 << 0, | ||||
|   META_MAXIMIZE_VERTICAL   = 1 << 1, | ||||
|   META_MAXIMIZE_BOTH       = (1 << 0 | 1 << 1), | ||||
|   META_MAXIMIZE_VERTICAL   = 1 << 1 | ||||
| } MetaMaximizeFlags; | ||||
|  | ||||
| /** | ||||
| @@ -254,13 +252,6 @@ void meta_window_begin_grab_op (MetaWindow *window, | ||||
|                                 gboolean    frame_action, | ||||
|                                 guint32     timestamp); | ||||
|  | ||||
| gboolean meta_window_can_maximize (MetaWindow *window); | ||||
| gboolean meta_window_can_minimize (MetaWindow *window); | ||||
| gboolean meta_window_can_shade (MetaWindow *window); | ||||
| gboolean meta_window_can_close (MetaWindow *window); | ||||
| gboolean meta_window_is_always_on_all_workspaces (MetaWindow *window); | ||||
| gboolean meta_window_is_above (MetaWindow *window); | ||||
| gboolean meta_window_allows_move (MetaWindow *window); | ||||
| gboolean meta_window_allows_resize (MetaWindow *window); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| _Name=Mutter (wayland compositor) | ||||
| Exec=mutter --wayland --display-server | ||||
| Exec=mutter-launch -- mutter --wayland | ||||
| NoDisplay=true | ||||
| # name of loadable control center module | ||||
| X-GNOME-WMSettingsModule=metacity | ||||
|   | ||||
| @@ -1,44 +0,0 @@ | ||||
| <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||||
| "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||||
| <node> | ||||
|   <interface name="org.freedesktop.login1.Session"> | ||||
|     <property name="Active" type="b" access="read" /> | ||||
|  | ||||
|     <method name="Activate"> | ||||
|     </method> | ||||
|     <method name="TakeControl"> | ||||
|       <arg name="force" type="b"/> | ||||
|     </method> | ||||
|     <method name="TakeDevice"> | ||||
|       <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/> | ||||
|       <arg name="major" type="u" direction="in"/> | ||||
|       <arg name="minor" type="u" direction="in"/> | ||||
|       <arg name="fd" type="h" direction="out"/> | ||||
|       <arg name="paused" type="b" direction="out"/> | ||||
|     </method> | ||||
|     <method name="ReleaseDevice"> | ||||
|       <arg name="major" type="u"/> | ||||
|       <arg name="minor" type="u"/> | ||||
|     </method> | ||||
|     <method name="PauseDeviceComplete"> | ||||
|       <arg name="major" type="u"/> | ||||
|       <arg name="minor" type="u"/> | ||||
|     </method> | ||||
|     <signal name="PauseDevice"> | ||||
|       <arg name="major" type="u"/> | ||||
|       <arg name="minor" type="u"/> | ||||
|       <arg name="type" type="s"/> | ||||
|     </signal> | ||||
|     <signal name="ResumeDevice"> | ||||
|       <arg name="major" type="u"/> | ||||
|       <arg name="minor" type="u"/> | ||||
|       <arg name="fd" type="h"/> | ||||
|     </signal> | ||||
|   </interface> | ||||
|  | ||||
|   <interface name="org.freedesktop.login1.Seat"> | ||||
|     <method name="SwitchTo"> | ||||
|       <arg name="vt" type="u"/> | ||||
|     </method> | ||||
|   </interface> | ||||
| </node> | ||||
| @@ -44,6 +44,8 @@ | ||||
| static void meta_frames_destroy       (GtkWidget       *object); | ||||
| static void meta_frames_finalize      (GObject         *object); | ||||
| static void meta_frames_style_updated (GtkWidget       *widget); | ||||
| static void meta_frames_map           (GtkWidget       *widget); | ||||
| static void meta_frames_unmap         (GtkWidget       *widget); | ||||
|  | ||||
| static void meta_frames_update_prelit_control (MetaFrames      *frames, | ||||
|                                                MetaUIFrame     *frame, | ||||
| @@ -132,6 +134,9 @@ meta_frames_class_init (MetaFramesClass *class) | ||||
|  | ||||
|   widget_class->style_updated = meta_frames_style_updated; | ||||
|  | ||||
|   widget_class->map = meta_frames_map; | ||||
|   widget_class->unmap = meta_frames_unmap; | ||||
|    | ||||
|   widget_class->draw = meta_frames_draw; | ||||
|   widget_class->destroy_event = meta_frames_destroy_event;   | ||||
|   widget_class->button_press_event = meta_frames_button_press_event; | ||||
| @@ -226,7 +231,6 @@ meta_frames_init (MetaFrames *frames) | ||||
|  | ||||
|   frames->style_variants = g_hash_table_new_full (g_str_hash, g_str_equal, | ||||
|                                                   g_free, g_object_unref); | ||||
|  | ||||
|   update_style_contexts (frames); | ||||
|  | ||||
|   gtk_widget_set_double_buffered (GTK_WIDGET (frames), FALSE); | ||||
| @@ -518,26 +522,13 @@ MetaFrames* | ||||
| meta_frames_new (int screen_number) | ||||
| { | ||||
|   GdkScreen *screen; | ||||
|   MetaFrames *frames; | ||||
|  | ||||
|   screen = gdk_display_get_screen (gdk_display_get_default (), | ||||
|                                    screen_number); | ||||
|  | ||||
|   frames = g_object_new (META_TYPE_FRAMES, | ||||
|                          "screen", screen, | ||||
|                          "type", GTK_WINDOW_POPUP, | ||||
|                          NULL); | ||||
|  | ||||
|   /* Put the window at an arbitrary offscreen location; the one place | ||||
|    * it can't be is at -100x-100, since the meta_window_new() will | ||||
|    * mistake it for a window created via meta_create_offscreen_window() | ||||
|    * and ignore it, and we need this window to get frame-synchronization | ||||
|    * messages so that GTK+'s style change handling works. | ||||
|    */ | ||||
|   gtk_window_move (GTK_WINDOW (frames), -200, -200); | ||||
|   gtk_window_resize (GTK_WINDOW (frames), 1, 1); | ||||
|  | ||||
|   return frames; | ||||
|   return g_object_new (META_TYPE_FRAMES, | ||||
|                        "screen", screen, | ||||
|                        NULL);   | ||||
| } | ||||
|  | ||||
| /* In order to use a style with a window it has to be attached to that | ||||
| @@ -644,6 +635,22 @@ meta_frames_unmanage_window (MetaFrames *frames, | ||||
|     meta_warning ("Frame 0x%lx not managed, can't unmanage\n", xwindow); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_frames_map (GtkWidget *widget) | ||||
| { | ||||
|   /* We override the parent map function to a no-op because we don't | ||||
|    * want to actually show the GDK window. But GTK needs to think that | ||||
|    * the widget is mapped or it won't deliver the events we care about. | ||||
|    */ | ||||
|   gtk_widget_set_mapped (widget, TRUE); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_frames_unmap (GtkWidget *widget) | ||||
| { | ||||
|   gtk_widget_set_mapped (widget, FALSE); | ||||
| } | ||||
|  | ||||
| static MetaUIFrame* | ||||
| meta_frames_lookup_window (MetaFrames *frames, | ||||
|                            Window      xwindow) | ||||
| @@ -1106,6 +1113,9 @@ meta_frame_titlebar_event (MetaUIFrame    *frame, | ||||
|     case G_DESKTOP_TITLEBAR_ACTION_MENU: | ||||
|       meta_core_show_window_menu (display, | ||||
|                                   frame->xwindow, | ||||
|                                   event->x_root, | ||||
|                                   event->y_root, | ||||
|                                   event->button, | ||||
|                                   event->time); | ||||
|       break; | ||||
|     } | ||||
| @@ -1265,9 +1275,30 @@ meta_frames_button_press_event (GtkWidget      *widget, | ||||
|       redraw_control (frames, frame, control); | ||||
|  | ||||
|       if (op == META_GRAB_OP_CLICKING_MENU) | ||||
|         meta_core_show_window_menu (display, | ||||
|                                     frame->xwindow, | ||||
|                                     event->time); | ||||
|         { | ||||
|           MetaFrameGeometry fgeom; | ||||
|           GdkRectangle *rect; | ||||
|           int dx, dy; | ||||
|            | ||||
|           meta_frames_calc_geometry (frames, frame, &fgeom); | ||||
|            | ||||
|           rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom); | ||||
|  | ||||
|           /* get delta to convert to root coords */ | ||||
|           dx = event->x_root - event->x; | ||||
|           dy = event->y_root - event->y; | ||||
|            | ||||
|           /* Align to the right end of the menu rectangle if RTL */ | ||||
|           if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) | ||||
|             dx += rect->width; | ||||
|  | ||||
|           meta_core_show_window_menu (display, | ||||
|                                       frame->xwindow, | ||||
|                                       rect->x + dx, | ||||
|                                       rect->y + rect->height + dy, | ||||
|                                       event->button, | ||||
|                                       event->time); | ||||
|         } | ||||
|     } | ||||
|   else if (event->button == 1 && | ||||
|            (control == META_FRAME_CONTROL_RESIZE_SE || | ||||
|   | ||||
							
								
								
									
										518
									
								
								src/ui/menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								src/ui/menu.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,518 @@ | ||||
| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | ||||
|  | ||||
| /* Mutter window menu */ | ||||
|  | ||||
| /*  | ||||
|  * Copyright (C) 2001 Havoc Pennington | ||||
|  * Copyright (C) 2004 Rob Adams | ||||
|  * Copyright (C) 2005 Elijah Newren | ||||
|  *  | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include "menu.h" | ||||
| #include <meta/main.h> | ||||
| #include "util-private.h" | ||||
| #include "core.h" | ||||
| #include "metaaccellabel.h" | ||||
| #include "ui.h" | ||||
|  | ||||
| typedef struct _MenuItem MenuItem; | ||||
| typedef struct _MenuData MenuData; | ||||
|  | ||||
| typedef enum | ||||
| { | ||||
|   MENU_ITEM_SEPARATOR = 0, | ||||
|   MENU_ITEM_NORMAL, | ||||
|   MENU_ITEM_CHECKBOX, | ||||
|   MENU_ITEM_RADIOBUTTON, | ||||
|   MENU_ITEM_WORKSPACE_LIST, | ||||
| } MetaMenuItemType; | ||||
|  | ||||
| struct _MenuItem | ||||
| { | ||||
|   MetaMenuOp op; | ||||
|   MetaMenuItemType type; | ||||
|   const gboolean checked; | ||||
|   const char *label; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct _MenuData | ||||
| { | ||||
|   MetaWindowMenu *menu; | ||||
|   MetaMenuOp op; | ||||
| }; | ||||
|  | ||||
| static void activate_cb (GtkWidget *menuitem, gpointer data); | ||||
|  | ||||
| static MenuItem menuitems[] = { | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MINIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Mi_nimize") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Ma_ximize") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_UNMAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Unma_ximize") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_SHADE, MENU_ITEM_NORMAL, FALSE, N_("Roll _Up") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, FALSE, N_("_Unroll") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MOVE, MENU_ITEM_NORMAL, FALSE, N_("_Move") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, FALSE, N_("_Resize") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, FALSE, N_("Move Titlebar On_screen") }, | ||||
|   { META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */ | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, FALSE, N_("Always on _Top") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, TRUE, N_("Always on _Top") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, FALSE, N_("_Always on Visible Workspace") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, FALSE,  N_("_Only on This Workspace") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Left") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace R_ight") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Up") }, | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Down") }, | ||||
|   { 0, MENU_ITEM_WORKSPACE_LIST, FALSE, NULL }, | ||||
|   { 0, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */ | ||||
|   /* Translators: Translate this string the same way as you do in libwnck! */ | ||||
|   { META_MENU_OP_DELETE, MENU_ITEM_NORMAL, FALSE, N_("_Close") } | ||||
| }; | ||||
|  | ||||
| static void | ||||
| popup_position_func (GtkMenu   *menu, | ||||
|                      gint      *x, | ||||
|                      gint      *y, | ||||
|                      gboolean  *push_in, | ||||
|                      gpointer  user_data) | ||||
| { | ||||
|   GtkRequisition req;       | ||||
|   GdkPoint *pos; | ||||
|  | ||||
|   pos = user_data; | ||||
|    | ||||
|   gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL); | ||||
|  | ||||
|   *x = pos->x; | ||||
|   *y = pos->y; | ||||
|    | ||||
|   if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) | ||||
|     *x = MAX (0, *x - req.width);  | ||||
|  | ||||
|   /* Ensure onscreen */ | ||||
|   *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width)); | ||||
|   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| menu_closed (GtkMenu *widget, | ||||
|              gpointer data) | ||||
| { | ||||
|   MetaWindowMenu *menu; | ||||
|    | ||||
|   menu = data; | ||||
|  | ||||
|   meta_frames_notify_menu_hide (menu->frames); | ||||
|   (* menu->func) (menu, | ||||
|                   GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), | ||||
|                   menu->client_xwindow, | ||||
|                   gtk_get_current_event_time (), | ||||
|                   0, 0, | ||||
|                   menu->data); | ||||
|    | ||||
|   /* menu may now be freed */ | ||||
| } | ||||
|  | ||||
| static void | ||||
| activate_cb (GtkWidget *menuitem, gpointer data) | ||||
| { | ||||
|   MenuData *md; | ||||
|    | ||||
|   g_return_if_fail (GTK_IS_WIDGET (menuitem)); | ||||
|    | ||||
|   md = data; | ||||
|  | ||||
|   meta_frames_notify_menu_hide (md->menu->frames); | ||||
|   (* md->menu->func) (md->menu, | ||||
|                       GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), | ||||
|                       md->menu->client_xwindow, | ||||
|                       gtk_get_current_event_time (), | ||||
|                       md->op, | ||||
|                       GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem), | ||||
|                                                           "workspace")), | ||||
|                       md->menu->data); | ||||
|  | ||||
|   /* menu may now be freed */ | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Given a Display and an index, get the workspace name and add any | ||||
|  * accelerators. At the moment this means adding a _ if the name is of | ||||
|  * the form "Workspace n" where n is less than 10, and escaping any | ||||
|  * other '_'s so they do not create inadvertant accelerators. | ||||
|  *  | ||||
|  * The calling code owns the string, and is reponsible to free the | ||||
|  * memory after use. | ||||
|  * | ||||
|  * See also http://mail.gnome.org/archives/gnome-i18n/2008-March/msg00380.html | ||||
|  * which discusses possible i18n concerns. | ||||
|  */ | ||||
| static char* | ||||
| get_workspace_name_with_accel (Display *display, | ||||
|                                Window   xroot, | ||||
|                                int      index) | ||||
| { | ||||
|   const char *name; | ||||
|   int number; | ||||
|   int charcount=0; | ||||
|  | ||||
|   name = meta_core_get_workspace_name_with_index (display, xroot, index); | ||||
|  | ||||
|   g_assert (name != NULL); | ||||
|    | ||||
|   /* | ||||
|    * If the name is of the form "Workspace x" where x is an unsigned | ||||
|    * integer, insert a '_' before the number if it is less than 10 and | ||||
|    * return it | ||||
|    */ | ||||
|   number = 0; | ||||
|   if (sscanf (name, _("Workspace %d%n"), &number, &charcount) != 0 && | ||||
|       *(name + charcount)=='\0') | ||||
|     { | ||||
|       char *new_name; | ||||
|        | ||||
|       /* | ||||
|        * Above name is a pointer into the Workspace struct. Here we make | ||||
|        * a copy copy so we can have our wicked way with it. | ||||
|        */ | ||||
|       if (number == 10) | ||||
|         new_name = g_strdup_printf (_("Workspace 1_0")); | ||||
|       else | ||||
|         new_name = g_strdup_printf (_("Workspace %s%d"), | ||||
|                                     number < 10 ? "_" : "", | ||||
|                                     number); | ||||
|       return new_name; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       /* | ||||
|        * Otherwise this is just a normal name. Escape any _ characters so that | ||||
|        * the user's workspace names do not get mangled.  If the number is less | ||||
|        * than 10 we provide an accelerator. | ||||
|        */ | ||||
|       char *new_name; | ||||
|       const char *source; | ||||
|       char *dest; | ||||
|  | ||||
|       /* | ||||
|        * Assume the worst case, that every character is a _.  We also | ||||
|        * provide memory for " (_#)" | ||||
|        */ | ||||
|       new_name = g_malloc0 (strlen (name) * 2 + 6 + 1); | ||||
|  | ||||
|       /* | ||||
|        * Now iterate down the strings, adding '_' to escape as we go | ||||
|        */ | ||||
|       dest = new_name; | ||||
|       source = name; | ||||
|       while (*source != '\0') | ||||
|         { | ||||
|           if (*source == '_') | ||||
|             *dest++ = '_'; | ||||
|           *dest++ = *source++; | ||||
|         } | ||||
|  | ||||
|       /* People don't start at workspace 0, but workspace 1 */ | ||||
|       if (index < 9) | ||||
|         { | ||||
|           g_snprintf (dest, 6, " (_%d)", index + 1); | ||||
|         } | ||||
|       else if (index == 9) | ||||
|         { | ||||
|           g_snprintf (dest, 6, " (_0)"); | ||||
|         } | ||||
|  | ||||
|       return new_name; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static GtkWidget * | ||||
| menu_item_new (MenuItem *menuitem, int workspace_id) | ||||
| { | ||||
|   unsigned int key; | ||||
|   MetaVirtualModifier mods; | ||||
|   const char *i18n_label; | ||||
|   GtkWidget *mi; | ||||
|   GtkWidget *accel_label; | ||||
|  | ||||
|   if (menuitem->type == MENU_ITEM_NORMAL) | ||||
|     { | ||||
|       mi = gtk_menu_item_new (); | ||||
|     } | ||||
|   else if (menuitem->type == MENU_ITEM_CHECKBOX) | ||||
|     { | ||||
|       mi = gtk_check_menu_item_new (); | ||||
|        | ||||
|       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), | ||||
|                                       menuitem->checked); | ||||
|     }     | ||||
|   else if (menuitem->type == MENU_ITEM_RADIOBUTTON) | ||||
|     { | ||||
|       mi = gtk_check_menu_item_new (); | ||||
|  | ||||
|       gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (mi), | ||||
|                                              TRUE); | ||||
|       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), | ||||
|                                       menuitem->checked); | ||||
|     } | ||||
|   else if (menuitem->type == MENU_ITEM_WORKSPACE_LIST) | ||||
|     return NULL; | ||||
|   else | ||||
|     return gtk_separator_menu_item_new (); | ||||
|  | ||||
|   i18n_label = _(menuitem->label); | ||||
|   meta_core_get_menu_accelerator (menuitem->op, workspace_id, &key, &mods); | ||||
|  | ||||
|   accel_label = meta_accel_label_new_with_mnemonic (i18n_label); | ||||
|   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5); | ||||
|  | ||||
|   gtk_container_add (GTK_CONTAINER (mi), accel_label); | ||||
|   gtk_widget_show (accel_label); | ||||
|  | ||||
|   meta_accel_label_set_accelerator (META_ACCEL_LABEL (accel_label), | ||||
|                                     key, mods); | ||||
|    | ||||
|   return mi; | ||||
| } | ||||
|  | ||||
| MetaWindowMenu* | ||||
| meta_window_menu_new   (MetaFrames         *frames, | ||||
|                         MetaMenuOp          ops, | ||||
|                         MetaMenuOp          insensitive, | ||||
|                         Window              client_xwindow, | ||||
|                         unsigned long       active_workspace, | ||||
|                         int                 n_workspaces, | ||||
|                         MetaWindowMenuFunc  func, | ||||
|                         gpointer            data) | ||||
| { | ||||
|   int i; | ||||
|   MetaWindowMenu *menu; | ||||
|  | ||||
|   /* FIXME: Modifications to 'ops' should happen in meta_window_show_menu */ | ||||
|   if (n_workspaces < 2) | ||||
|     ops &= ~(META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES); | ||||
|   else if (n_workspaces == 2)  | ||||
|     /* #151183: If we only have two workspaces, disable the menu listing them. */ | ||||
|     ops &= ~(META_MENU_OP_WORKSPACES); | ||||
|    | ||||
|   menu = g_new (MetaWindowMenu, 1); | ||||
|   menu->frames = frames; | ||||
|   menu->client_xwindow = client_xwindow; | ||||
|   menu->func = func; | ||||
|   menu->data = data; | ||||
|   menu->ops = ops; | ||||
|   menu->insensitive = insensitive;   | ||||
|    | ||||
|   menu->menu = gtk_menu_new (); | ||||
|  | ||||
|   gtk_menu_set_screen (GTK_MENU (menu->menu), | ||||
|                        gtk_widget_get_screen (GTK_WIDGET (frames))); | ||||
|  | ||||
|   for (i = 0; i < (int) G_N_ELEMENTS (menuitems); i++) | ||||
|     { | ||||
|       MenuItem menuitem = menuitems[i]; | ||||
|       if (ops & menuitem.op || menuitem.op == 0) | ||||
|         { | ||||
|           GtkWidget *mi; | ||||
|           MenuData *md; | ||||
|           unsigned int key; | ||||
|           MetaVirtualModifier mods; | ||||
|  | ||||
|           mi = menu_item_new (&menuitem, -1); | ||||
|  | ||||
|           /* Set the activeness of radiobuttons. */ | ||||
|           switch (menuitem.op) | ||||
|             { | ||||
|             case META_MENU_OP_STICK: | ||||
|               gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), | ||||
|                                               active_workspace == 0xFFFFFFFF); | ||||
|               break; | ||||
|             case META_MENU_OP_UNSTICK: | ||||
|               gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), | ||||
|                                               active_workspace != 0xFFFFFFFF); | ||||
|               break; | ||||
|             default: | ||||
|               break; | ||||
|             } | ||||
|  | ||||
|           if (menuitem.type == MENU_ITEM_WORKSPACE_LIST) | ||||
|             { | ||||
|               if (ops & META_MENU_OP_WORKSPACES) | ||||
|                 { | ||||
|                   Display *display; | ||||
|                   Window xroot; | ||||
|                   GdkScreen *screen; | ||||
|                   GdkWindow *window; | ||||
|                   GtkWidget *submenu; | ||||
|                   int j; | ||||
|  | ||||
|                   MenuItem to_another_workspace = { | ||||
|                     0, MENU_ITEM_NORMAL, FALSE, | ||||
|                     N_("Move to Another _Workspace") | ||||
|                   }; | ||||
|  | ||||
|                   meta_verbose ("Creating %d-workspace menu current space %lu\n", | ||||
|                       n_workspaces, active_workspace); | ||||
|  | ||||
|                   window = gtk_widget_get_window (GTK_WIDGET (frames)); | ||||
|                   display = GDK_WINDOW_XDISPLAY (window); | ||||
|  | ||||
|                   screen = gdk_window_get_screen (window); | ||||
|                   xroot = GDK_WINDOW_XID (gdk_screen_get_root_window (screen)); | ||||
|  | ||||
|                   submenu = gtk_menu_new (); | ||||
|  | ||||
|                   g_assert (mi==NULL); | ||||
|                   mi = menu_item_new (&to_another_workspace, -1); | ||||
|                   gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu); | ||||
|  | ||||
|                   for (j = 0; j < n_workspaces; j++) | ||||
|                     { | ||||
|                       char *label; | ||||
|                       MenuData *md; | ||||
|                       unsigned int key; | ||||
|                       MetaVirtualModifier mods; | ||||
|                       MenuItem moveitem; | ||||
|                       GtkWidget *submi; | ||||
|  | ||||
|                       meta_core_get_menu_accelerator (META_MENU_OP_WORKSPACES, | ||||
|                           j + 1, | ||||
|                           &key, &mods); | ||||
|  | ||||
|                       label = get_workspace_name_with_accel (display, xroot, j); | ||||
|  | ||||
|                       moveitem.type = MENU_ITEM_NORMAL; | ||||
|                       moveitem.op = META_MENU_OP_WORKSPACES; | ||||
|                       moveitem.label = label; | ||||
|                       submi = menu_item_new (&moveitem, j + 1); | ||||
|  | ||||
|                       g_free (label); | ||||
|  | ||||
|                       if ((active_workspace == (unsigned)j) && (ops & META_MENU_OP_UNSTICK)) | ||||
|                         gtk_widget_set_sensitive (submi, FALSE); | ||||
|  | ||||
|                       md = g_new (MenuData, 1); | ||||
|  | ||||
|                       md->menu = menu; | ||||
|                       md->op = META_MENU_OP_WORKSPACES; | ||||
|  | ||||
|                       g_object_set_data (G_OBJECT (submi), | ||||
|                           "workspace", | ||||
|                           GINT_TO_POINTER (j)); | ||||
|  | ||||
|                       g_signal_connect_data (G_OBJECT (submi), | ||||
|                           "activate", | ||||
|                           G_CALLBACK (activate_cb), | ||||
|                           md, | ||||
|                           (GClosureNotify) g_free, 0); | ||||
|  | ||||
|                       gtk_menu_shell_append (GTK_MENU_SHELL (submenu), submi); | ||||
|  | ||||
|                       gtk_widget_show (submi); | ||||
|                     } | ||||
|                   } | ||||
|                 else | ||||
|                   meta_verbose ("not creating workspace menu\n"); | ||||
|             } | ||||
|           else if (menuitem.type != MENU_ITEM_SEPARATOR) | ||||
|             { | ||||
|               meta_core_get_menu_accelerator (menuitems[i].op, -1, | ||||
|                                               &key, &mods); | ||||
|  | ||||
|               if (insensitive & menuitem.op) | ||||
|                 gtk_widget_set_sensitive (mi, FALSE); | ||||
|                | ||||
|               md = g_new (MenuData, 1); | ||||
|                | ||||
|               md->menu = menu; | ||||
|               md->op = menuitem.op; | ||||
|                | ||||
|               g_signal_connect_data (G_OBJECT (mi), | ||||
|                                      "activate", | ||||
|                                      G_CALLBACK (activate_cb), | ||||
|                                      md, | ||||
|                                      (GClosureNotify) g_free, 0); | ||||
|             } | ||||
|  | ||||
|           if (mi) | ||||
|             { | ||||
|               gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi); | ||||
|            | ||||
|               gtk_widget_show (mi); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
|   g_signal_connect (menu->menu, "selection_done", | ||||
|                     G_CALLBACK (menu_closed), menu);   | ||||
|  | ||||
|   return menu; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_menu_popup (MetaWindowMenu     *menu, | ||||
|                         int                 root_x, | ||||
|                         int                 root_y, | ||||
|                         int                 button, | ||||
|                         guint32             timestamp) | ||||
| { | ||||
|   GdkPoint *pt; | ||||
|    | ||||
|   pt = g_new (GdkPoint, 1); | ||||
|  | ||||
|   g_object_set_data_full (G_OBJECT (menu->menu), | ||||
|                           "destroy-point", | ||||
|                           pt, | ||||
|                           g_free); | ||||
|  | ||||
|   pt->x = root_x; | ||||
|   pt->y = root_y; | ||||
|    | ||||
|   gtk_menu_popup (GTK_MENU (menu->menu), | ||||
|                   NULL, NULL, | ||||
|                   popup_position_func, pt, | ||||
|                   button, | ||||
|                   timestamp); | ||||
|  | ||||
|   if (!gtk_widget_get_visible (menu->menu)) | ||||
|     meta_warning ("GtkMenu failed to grab the pointer\n"); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_window_menu_free (MetaWindowMenu *menu) | ||||
| { | ||||
|   gtk_widget_destroy (menu->menu); | ||||
|   g_free (menu); | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/ui/menu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/ui/menu.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | ||||
|  | ||||
| /* Mutter window menu */ | ||||
|  | ||||
| /*  | ||||
|  * Copyright (C) 2001 Havoc Pennington | ||||
|  *  | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  *  | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef META_MENU_H | ||||
| #define META_MENU_H | ||||
|  | ||||
| #include <gtk/gtk.h> | ||||
| #include "frames.h" | ||||
|  | ||||
| struct _MetaWindowMenu | ||||
| { | ||||
|   MetaFrames *frames; | ||||
|   Window client_xwindow; | ||||
|   GtkWidget *menu; | ||||
|   MetaWindowMenuFunc func; | ||||
|   gpointer data; | ||||
|   MetaMenuOp ops; | ||||
|   MetaMenuOp insensitive; | ||||
| }; | ||||
|  | ||||
| MetaWindowMenu* meta_window_menu_new      (MetaFrames         *frames, | ||||
|                                            MetaMenuOp          ops, | ||||
|                                            MetaMenuOp          insensitive, | ||||
|                                            Window              client_xwindow, | ||||
|                                            unsigned long       active_workspace, | ||||
|                                            int                 n_workspaces, | ||||
|                                            MetaWindowMenuFunc  func, | ||||
|                                            gpointer            data); | ||||
| void            meta_window_menu_popup    (MetaWindowMenu     *menu, | ||||
|                                            int                 root_x, | ||||
|                                            int                 root_y, | ||||
|                                            int                 button, | ||||
|                                            guint32             timestamp); | ||||
| void            meta_window_menu_free     (MetaWindowMenu     *menu); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										451
									
								
								src/ui/metaaccellabel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								src/ui/metaaccellabel.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,451 @@ | ||||
| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | ||||
|  | ||||
| /* Metacity hacked-up GtkAccelLabel */ | ||||
| /* Copyright (C) 2002 Red Hat, Inc. */ | ||||
| /* GTK - The GIMP Toolkit | ||||
|  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald | ||||
|  * | ||||
|  * MetaAccelLabel: GtkLabel with accelerator monitoring facilities. | ||||
|  * Copyright (C) 1998 Tim Janik | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS | ||||
|  * file for a list of people on the GTK+ Team.  See the ChangeLog | ||||
|  * files for a list of changes.  These files are distributed with | ||||
|  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
| #include "metaaccellabel.h" | ||||
| #include <gtk/gtk.h> | ||||
| #include <string.h> | ||||
| #include "util-private.h" | ||||
|  | ||||
| static void     meta_accel_label_destroy      (GtkWidget           *object); | ||||
| static void     meta_accel_label_finalize     (GObject             *object); | ||||
| static void     meta_accel_label_get_preferred_width  (GtkWidget *widget, | ||||
|                                                        gint      *minimum, | ||||
|                                                        gint      *natural); | ||||
| static void     meta_accel_label_get_preferred_height (GtkWidget *widget, | ||||
|                                                        gint      *minimum, | ||||
|                                                        gint      *natural); | ||||
| static gboolean meta_accel_label_draw         (GtkWidget           *widget, | ||||
|                                                cairo_t             *cr); | ||||
|  | ||||
| static void  meta_accel_label_update          (MetaAccelLabel *accel_label); | ||||
| static int   meta_accel_label_get_accel_width (MetaAccelLabel *accel_label); | ||||
|  | ||||
| G_DEFINE_TYPE (MetaAccelLabel, meta_accel_label, GTK_TYPE_LABEL); | ||||
|  | ||||
| static void | ||||
| meta_accel_label_class_init (MetaAccelLabelClass *class) | ||||
| { | ||||
|   GObjectClass *gobject_class = G_OBJECT_CLASS (class); | ||||
|   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); | ||||
|  | ||||
|   gobject_class->finalize = meta_accel_label_finalize; | ||||
|  | ||||
|   widget_class->destroy = meta_accel_label_destroy; | ||||
|  | ||||
|   widget_class->get_preferred_width = meta_accel_label_get_preferred_width; | ||||
|   widget_class->get_preferred_height = meta_accel_label_get_preferred_height; | ||||
|   widget_class->draw = meta_accel_label_draw; | ||||
|  | ||||
|   class->signal_quote1 = g_strdup ("<:"); | ||||
|   class->signal_quote2 = g_strdup (":>"); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the shift key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_shift = g_strdup (_("Shift")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the control key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_control = g_strdup (_("Ctrl")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the alt key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_alt = g_strdup (_("Alt")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the meta key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_meta = g_strdup (_("Meta")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the super key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_super = g_strdup (_("Super")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the hyper key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_hyper = g_strdup (_("Hyper")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the mod2 key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_mod2 = g_strdup (_("Mod2")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the mod3 key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_mod3 = g_strdup (_("Mod3")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the mod4 key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_mod4 = g_strdup (_("Mod4")); | ||||
|   /* This is the text that should appear next to menu accelerators | ||||
|    * that use the mod5 key. If the text on this key isn't typically | ||||
|    * translated on keyboards used for your language, don't translate | ||||
|    * this. | ||||
|    */ | ||||
|   class->mod_name_mod5 = g_strdup (_("Mod5")); | ||||
|  | ||||
|   class->mod_separator = g_strdup ("+"); | ||||
|   class->accel_seperator = g_strdup (" / "); | ||||
|   class->latin1_to_char = TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_init (MetaAccelLabel *accel_label) | ||||
| { | ||||
|   accel_label->accel_padding = 3; | ||||
|   accel_label->accel_string = NULL; | ||||
|  | ||||
|   meta_accel_label_update (accel_label); | ||||
| } | ||||
|  | ||||
| GtkWidget* | ||||
| meta_accel_label_new_with_mnemonic (const gchar *string) | ||||
| { | ||||
|   MetaAccelLabel *accel_label; | ||||
|  | ||||
|   g_return_val_if_fail (string != NULL, NULL); | ||||
|  | ||||
|   accel_label = g_object_new (META_TYPE_ACCEL_LABEL, NULL); | ||||
|  | ||||
|   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), string); | ||||
|  | ||||
|   return GTK_WIDGET (accel_label); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_destroy (GtkWidget *object) | ||||
| { | ||||
|   MetaAccelLabel *accel_label = META_ACCEL_LABEL (object); | ||||
|  | ||||
|  | ||||
|   g_free (accel_label->accel_string); | ||||
|   accel_label->accel_string = NULL; | ||||
|  | ||||
|   accel_label->accel_mods = 0; | ||||
|   accel_label->accel_key = 0; | ||||
|  | ||||
|   GTK_WIDGET_CLASS (meta_accel_label_parent_class)->destroy (object); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_finalize (GObject *object) | ||||
| { | ||||
|   MetaAccelLabel *accel_label = META_ACCEL_LABEL (object); | ||||
|  | ||||
|   g_free (accel_label->accel_string); | ||||
|  | ||||
|   G_OBJECT_CLASS (meta_accel_label_parent_class)->finalize (object); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_accel_label_set_accelerator (MetaAccelLabel         *accel_label, | ||||
|                                   guint                   accelerator_key, | ||||
|                                   MetaVirtualModifier     accelerator_mods) | ||||
| { | ||||
|   g_return_if_fail (META_IS_ACCEL_LABEL (accel_label)); | ||||
|  | ||||
|   if (accelerator_key != accel_label->accel_key || | ||||
|       accelerator_mods != accel_label->accel_mods) | ||||
|     { | ||||
|       accel_label->accel_mods = accelerator_mods; | ||||
|       accel_label->accel_key = accelerator_key; | ||||
|  | ||||
|       meta_accel_label_update (accel_label); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int | ||||
| meta_accel_label_get_accel_width (MetaAccelLabel *accel_label) | ||||
| { | ||||
|   g_return_val_if_fail (META_IS_ACCEL_LABEL (accel_label), 0); | ||||
|  | ||||
|   return (accel_label->accel_string_width + | ||||
| 	  (accel_label->accel_string_width ? accel_label->accel_padding : 0)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_get_preferred_width (GtkWidget *widget, | ||||
|                                       gint      *minimum, | ||||
|                                       gint      *natural) | ||||
| { | ||||
|   MetaAccelLabel *accel_label = META_ACCEL_LABEL (widget); | ||||
|   PangoLayout *layout; | ||||
|   gint width; | ||||
|  | ||||
|   GTK_WIDGET_CLASS (meta_accel_label_parent_class)->get_preferred_width (widget, minimum, natural); | ||||
|  | ||||
|   layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string); | ||||
|   pango_layout_get_pixel_size (layout, &width, NULL); | ||||
|   accel_label->accel_string_width = width; | ||||
|  | ||||
|   g_object_unref (G_OBJECT (layout)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_get_preferred_height (GtkWidget *widget, | ||||
|                                        gint      *minimum, | ||||
|                                        gint      *natural) | ||||
| { | ||||
|   GTK_WIDGET_CLASS (meta_accel_label_parent_class)->get_preferred_height (widget, minimum, natural); | ||||
| } | ||||
|  | ||||
| /* Mostly taken from GTK3. */ | ||||
| static gboolean | ||||
| meta_accel_label_draw (GtkWidget *widget, | ||||
|                        cairo_t   *cr) | ||||
| { | ||||
|   MetaAccelLabel *accel_label = META_ACCEL_LABEL (widget); | ||||
|   GtkMisc *misc = GTK_MISC (accel_label); | ||||
|   GtkTextDirection direction; | ||||
|   int ac_width; | ||||
|   GtkAllocation allocation; | ||||
|   GtkRequisition requisition; | ||||
|  | ||||
|   direction = gtk_widget_get_direction (widget); | ||||
|   ac_width = meta_accel_label_get_accel_width (accel_label); | ||||
|   gtk_widget_get_allocation (widget, &allocation); | ||||
|   gtk_widget_get_preferred_size (widget, | ||||
|                                  &requisition, NULL); | ||||
|  | ||||
|   if (allocation.width >= requisition.width + ac_width) | ||||
|     { | ||||
|       GtkStyleContext *style; | ||||
|       PangoLayout *label_layout; | ||||
|       PangoLayout *accel_layout; | ||||
|       GtkLabel *label = GTK_LABEL (widget); | ||||
|       gint x, y, xpad, ypad; | ||||
|       gfloat xalign, yalign; | ||||
|  | ||||
|       label_layout = gtk_label_get_layout (GTK_LABEL (accel_label)); | ||||
|       gtk_misc_get_alignment (misc, &xalign, &yalign); | ||||
|  | ||||
|       cairo_save (cr); | ||||
|  | ||||
|       /* XXX: Mad hack: We modify the label's width so it renders | ||||
|        * properly in its draw function that we chain to. */ | ||||
|       if (direction == GTK_TEXT_DIR_RTL) | ||||
|         cairo_translate (cr, ac_width, 0); | ||||
|       if (gtk_label_get_ellipsize (label)) | ||||
|         pango_layout_set_width (label_layout, | ||||
|                                 pango_layout_get_width (label_layout)  | ||||
|                                 - ac_width * PANGO_SCALE); | ||||
|        | ||||
|       allocation.width -= ac_width; | ||||
|       gtk_widget_set_allocation (widget, &allocation); | ||||
|       if (GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw) | ||||
|         GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw (widget, | ||||
|                                                                cr); | ||||
|       allocation.width += ac_width; | ||||
|       gtk_widget_set_allocation (widget, &allocation); | ||||
|       if (gtk_label_get_ellipsize (label)) | ||||
|         pango_layout_set_width (label_layout, | ||||
|                                 pango_layout_get_width (label_layout)  | ||||
|                                 + ac_width * PANGO_SCALE); | ||||
|  | ||||
|       cairo_restore (cr); | ||||
|  | ||||
|       gtk_misc_get_padding (misc, &xpad, &ypad); | ||||
|  | ||||
|       if (direction == GTK_TEXT_DIR_RTL) | ||||
|         x = xpad; | ||||
|       else | ||||
|         x = gtk_widget_get_allocated_width (widget) - xpad - ac_width; | ||||
|  | ||||
|       gtk_label_get_layout_offsets (GTK_LABEL (accel_label), NULL, &y); | ||||
|  | ||||
|       accel_layout = gtk_widget_create_pango_layout (widget, accel_label->accel_string); | ||||
|  | ||||
|       y = (allocation.height - (requisition.height - ypad * 2)) * yalign + 1.5; | ||||
|  | ||||
|       style = gtk_widget_get_style_context (widget); | ||||
|       gtk_style_context_save (style); | ||||
|       gtk_style_context_set_state (style, | ||||
|                                    gtk_widget_get_state_flags (widget)); | ||||
|       gtk_render_layout (gtk_widget_get_style_context (widget), | ||||
|                          cr, | ||||
|                          x, y, | ||||
|                          accel_layout); | ||||
|       gtk_style_context_restore (style); | ||||
|  | ||||
|       g_object_unref (accel_layout); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       if (GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw) | ||||
|         GTK_WIDGET_CLASS (meta_accel_label_parent_class)->draw (widget, cr); | ||||
|     } | ||||
|    | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_accel_label_update (MetaAccelLabel *accel_label) | ||||
| { | ||||
|   MetaAccelLabelClass *class; | ||||
|   GString *gstring; | ||||
|   gboolean seen_mod = FALSE; | ||||
|   gunichar ch; | ||||
|  | ||||
|   g_return_if_fail (META_IS_ACCEL_LABEL (accel_label)); | ||||
|  | ||||
|   class = META_ACCEL_LABEL_GET_CLASS (accel_label); | ||||
|  | ||||
|   g_free (accel_label->accel_string); | ||||
|   accel_label->accel_string = NULL; | ||||
|  | ||||
|   gstring = g_string_new (accel_label->accel_string); | ||||
|   g_string_append (gstring, gstring->len ? class->accel_seperator : "   "); | ||||
|  | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_SHIFT_MASK) | ||||
|     { | ||||
|       g_string_append (gstring, class->mod_name_shift); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_CONTROL_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_control); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_ALT_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_alt); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_META_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_meta); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_SUPER_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_super); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_HYPER_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_hyper); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_MOD2_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_mod2); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_MOD3_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_mod3); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_MOD4_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_mod4); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|   if (accel_label->accel_mods & META_VIRTUAL_MOD5_MASK) | ||||
|     { | ||||
|       if (seen_mod) | ||||
|         g_string_append (gstring, class->mod_separator); | ||||
|       g_string_append (gstring, class->mod_name_mod5); | ||||
|       seen_mod = TRUE; | ||||
|     } | ||||
|  | ||||
|   if (seen_mod) | ||||
|     g_string_append (gstring, class->mod_separator); | ||||
|  | ||||
|   ch = gdk_keyval_to_unicode (accel_label->accel_key); | ||||
|   if (ch && (g_unichar_isgraph (ch) || ch == ' ') && | ||||
|       (ch < 0x80 || class->latin1_to_char)) | ||||
|     { | ||||
|       switch (ch) | ||||
|         { | ||||
|         case ' ': | ||||
|           g_string_append (gstring, "Space"); | ||||
|           break; | ||||
|         case '\\': | ||||
|           g_string_append (gstring, "Backslash"); | ||||
|           break; | ||||
|         default: | ||||
|           g_string_append_unichar (gstring, g_unichar_toupper (ch)); | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       gchar *tmp; | ||||
|  | ||||
|       tmp = gtk_accelerator_name (accel_label->accel_key, 0); | ||||
|       if (tmp[0] != 0 && tmp[1] == 0) | ||||
|         tmp[0] = g_ascii_toupper (tmp[0]); | ||||
|       g_string_append (gstring, tmp); | ||||
|       g_free (tmp); | ||||
|     } | ||||
|  | ||||
|   g_free (accel_label->accel_string); | ||||
|   accel_label->accel_string = gstring->str; | ||||
|   g_string_free (gstring, FALSE); | ||||
|  | ||||
|   g_assert (accel_label->accel_string); | ||||
|   /* accel_label->accel_string = g_strdup ("-/-"); */ | ||||
|  | ||||
|   gtk_widget_queue_resize (GTK_WIDGET (accel_label)); | ||||
| } | ||||
							
								
								
									
										104
									
								
								src/ui/metaaccellabel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/ui/metaaccellabel.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ | ||||
|  | ||||
| /* Metacity hacked-up GtkAccelLabel */ | ||||
| /* Copyright (C) 2002 Red Hat, Inc. */ | ||||
| /* GTK - The GIMP Toolkit | ||||
|  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald | ||||
|  * | ||||
|  * MetaAccelLabel: GtkLabel with accelerator monitoring facilities. | ||||
|  * Copyright (C) 1998 Tim Janik | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS | ||||
|  * file for a list of people on the GTK+ Team.  See the ChangeLog | ||||
|  * files for a list of changes.  These files are distributed with | ||||
|  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. | ||||
|  */ | ||||
|  | ||||
| #ifndef __META_ACCEL_LABEL_H__ | ||||
| #define __META_ACCEL_LABEL_H__ | ||||
|  | ||||
| #include <gtk/gtk.h> | ||||
| #include <meta/common.h> | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif /* __cplusplus */ | ||||
|  | ||||
|  | ||||
| #define META_TYPE_ACCEL_LABEL		(meta_accel_label_get_type ()) | ||||
| #define META_ACCEL_LABEL(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_ACCEL_LABEL, MetaAccelLabel)) | ||||
| #define META_ACCEL_LABEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_ACCEL_LABEL, MetaAccelLabelClass)) | ||||
| #define META_IS_ACCEL_LABEL(obj)	 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_ACCEL_LABEL)) | ||||
| #define META_IS_ACCEL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_ACCEL_LABEL)) | ||||
| #define META_ACCEL_LABEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_ACCEL_LABEL, MetaAccelLabelClass)) | ||||
|  | ||||
|  | ||||
| typedef struct _MetaAccelLabel	    MetaAccelLabel; | ||||
| typedef struct _MetaAccelLabelClass  MetaAccelLabelClass; | ||||
|  | ||||
| struct _MetaAccelLabel | ||||
| { | ||||
|   GtkLabel label; | ||||
|  | ||||
|   MetaVirtualModifier accel_mods; | ||||
|   guint accel_key; | ||||
|   guint accel_padding; | ||||
|   gchar *accel_string; | ||||
|   guint16 accel_string_width; | ||||
| }; | ||||
|  | ||||
| struct _MetaAccelLabelClass | ||||
| { | ||||
|   GtkLabelClass	 parent_class; | ||||
|  | ||||
|   gchar		*signal_quote1; | ||||
|   gchar		*signal_quote2; | ||||
|   gchar		*mod_name_shift; | ||||
|   gchar		*mod_name_control; | ||||
|   gchar		*mod_name_alt; | ||||
|   gchar		*mod_name_meta; | ||||
|   gchar		*mod_name_super; | ||||
|   gchar		*mod_name_hyper; | ||||
|   gchar		*mod_name_mod2; | ||||
|   gchar		*mod_name_mod3; | ||||
|   gchar		*mod_name_mod4; | ||||
|   gchar		*mod_name_mod5; | ||||
|   gchar		*mod_separator; | ||||
|   gchar		*accel_seperator; | ||||
|   guint		 latin1_to_char : 1; | ||||
|  | ||||
|   /* Padding for future expansion */ | ||||
|   void (*_gtk_reserved1) (void); | ||||
|   void (*_gtk_reserved2) (void); | ||||
|   void (*_gtk_reserved3) (void); | ||||
|   void (*_gtk_reserved4) (void); | ||||
| }; | ||||
|  | ||||
| GType	   meta_accel_label_get_type          (void) G_GNUC_CONST; | ||||
| GtkWidget* meta_accel_label_new_with_mnemonic (const gchar            *string); | ||||
| void       meta_accel_label_set_accelerator   (MetaAccelLabel         *accel_label, | ||||
|                                                guint                   accelerator_key, | ||||
|                                                MetaVirtualModifier     accelerator_mods); | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif /* __cplusplus */ | ||||
|  | ||||
|  | ||||
| #endif /* __META_ACCEL_LABEL_H__ */ | ||||
							
								
								
									
										43
									
								
								src/ui/ui.c
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/ui/ui.c
									
									
									
									
									
								
							| @@ -295,12 +295,9 @@ meta_ui_new (Display *xdisplay, | ||||
|   g_assert (gdisplay == gdk_display_get_default ()); | ||||
|  | ||||
|   ui->frames = meta_frames_new (XScreenNumberOfScreen (screen)); | ||||
|   /* GTK+ needs the frame-sync protocol to work in order to properly | ||||
|    * handle style changes. This means that the dummy widget we create | ||||
|    * to get the style for title bars actually needs to be mapped | ||||
|    * and fully tracked as a MetaWindow. Horrible, but mostly harmless - | ||||
|    * the window is a 1x1 overide redirect window positioned offscreen. | ||||
|    */ | ||||
|   /* This does not actually show any widget. MetaFrames has been hacked so | ||||
|    * that showing it doesn't actually do anything. But we need the flags | ||||
|    * set for GTK to deliver events properly. */ | ||||
|   gtk_widget_show (GTK_WIDGET (ui->frames)); | ||||
|  | ||||
|   g_object_set_data (G_OBJECT (gdisplay), "meta-ui", ui); | ||||
| @@ -514,6 +511,40 @@ meta_ui_set_frame_title (MetaUI     *ui, | ||||
|   meta_frames_set_title (ui->frames, xwindow, title); | ||||
| } | ||||
|  | ||||
| MetaWindowMenu* | ||||
| meta_ui_window_menu_new  (MetaUI             *ui, | ||||
|                           Window              client_xwindow, | ||||
|                           MetaMenuOp          ops, | ||||
|                           MetaMenuOp          insensitive, | ||||
|                           unsigned long       active_workspace, | ||||
|                           int                 n_workspaces, | ||||
|                           MetaWindowMenuFunc  func, | ||||
|                           gpointer            data) | ||||
| { | ||||
|   return meta_window_menu_new (ui->frames, | ||||
|                                ops, insensitive, | ||||
|                                client_xwindow, | ||||
|                                active_workspace, | ||||
|                                n_workspaces, | ||||
|                                func, data); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_ui_window_menu_popup (MetaWindowMenu     *menu, | ||||
|                            int                 root_x, | ||||
|                            int                 root_y, | ||||
|                            int                 button, | ||||
|                            guint32             timestamp) | ||||
| { | ||||
|   meta_window_menu_popup (menu, root_x, root_y, button, timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_ui_window_menu_free (MetaWindowMenu *menu) | ||||
| { | ||||
|   meta_window_menu_free (menu); | ||||
| } | ||||
|  | ||||
| GdkPixbuf* | ||||
| meta_gdk_pixbuf_get_from_pixmap (Pixmap       xpixmap, | ||||
|                                  int          src_x, | ||||
|   | ||||
							
								
								
									
										15
									
								
								src/ui/ui.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/ui/ui.h
									
									
									
									
									
								
							| @@ -120,6 +120,21 @@ void meta_ui_update_frame_style (MetaUI  *ui, | ||||
| void meta_ui_repaint_frame (MetaUI *ui, | ||||
|                             Window xwindow); | ||||
|  | ||||
| MetaWindowMenu* meta_ui_window_menu_new   (MetaUI             *ui, | ||||
|                                            Window              client_xwindow, | ||||
|                                            MetaMenuOp          ops, | ||||
|                                            MetaMenuOp          insensitive, | ||||
|                                            unsigned long       active_workspace, | ||||
|                                            int                 n_workspaces, | ||||
|                                            MetaWindowMenuFunc  func, | ||||
|                                            gpointer            data); | ||||
| void            meta_ui_window_menu_popup (MetaWindowMenu     *menu, | ||||
|                                            int                 root_x, | ||||
|                                            int                 root_y, | ||||
|                                            int                 button, | ||||
|                                            guint32             timestamp); | ||||
| void            meta_ui_window_menu_free  (MetaWindowMenu     *menu); | ||||
|  | ||||
|  | ||||
| /* FIXME these lack a display arg */ | ||||
| GdkPixbuf* meta_gdk_pixbuf_get_from_pixmap (Pixmap       xpixmap, | ||||
|   | ||||
| @@ -1,441 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include "meta-login1.h" | ||||
|  | ||||
| #include "meta-dbus-login1.h" | ||||
|  | ||||
| #include "meta-wayland-private.h" | ||||
| #include "meta-cursor-tracker-private.h" | ||||
|  | ||||
| #include <gio/gunixfdlist.h> | ||||
|  | ||||
| #include <clutter/clutter.h> | ||||
| #include <clutter/evdev/clutter-evdev.h> | ||||
| #include <clutter/egl/clutter-egl.h> | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <malloc.h> | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <systemd/sd-login.h> | ||||
|  | ||||
| struct _MetaLogin1 | ||||
| { | ||||
|   Login1Session *session_proxy; | ||||
|   Login1Seat *seat_proxy; | ||||
|  | ||||
|   gboolean session_active; | ||||
| }; | ||||
|  | ||||
| /* Stolen from tp_escape_as_identifier, from tp-glib, | ||||
|  * which follows the same escaping convention as systemd. | ||||
|  */ | ||||
| static inline gboolean | ||||
| _esc_ident_bad (gchar c, gboolean is_first) | ||||
| { | ||||
|   return ((c < 'a' || c > 'z') && | ||||
|           (c < 'A' || c > 'Z') && | ||||
|           (c < '0' || c > '9' || is_first)); | ||||
| } | ||||
|  | ||||
| static gchar * | ||||
| escape_dbus_component (const gchar *name) | ||||
| { | ||||
|   gboolean bad = FALSE; | ||||
|   size_t len = 0; | ||||
|   GString *op; | ||||
|   const gchar *ptr, *first_ok; | ||||
|  | ||||
|   g_return_val_if_fail (name != NULL, NULL); | ||||
|  | ||||
|   /* fast path for empty name */ | ||||
|   if (name[0] == '\0') | ||||
|     return g_strdup ("_"); | ||||
|  | ||||
|   for (ptr = name; *ptr; ptr++) | ||||
|     { | ||||
|       if (_esc_ident_bad (*ptr, ptr == name)) | ||||
|         { | ||||
|           bad = TRUE; | ||||
|           len += 3; | ||||
|         } | ||||
|       else | ||||
|         len++; | ||||
|     } | ||||
|  | ||||
|   /* fast path if it's clean */ | ||||
|   if (!bad) | ||||
|     return g_strdup (name); | ||||
|  | ||||
|   /* If strictly less than ptr, first_ok is the first uncopied safe character. | ||||
|    */ | ||||
|   first_ok = name; | ||||
|   op = g_string_sized_new (len); | ||||
|   for (ptr = name; *ptr; ptr++) | ||||
|     { | ||||
|       if (_esc_ident_bad (*ptr, ptr == name)) | ||||
|         { | ||||
|           /* copy preceding safe characters if any */ | ||||
|           if (first_ok < ptr) | ||||
|             { | ||||
|               g_string_append_len (op, first_ok, ptr - first_ok); | ||||
|             } | ||||
|           /* escape the unsafe character */ | ||||
|           g_string_append_printf (op, "_%02x", (unsigned char)(*ptr)); | ||||
|           /* restart after it */ | ||||
|           first_ok = ptr + 1; | ||||
|         } | ||||
|     } | ||||
|   /* copy trailing safe characters if any */ | ||||
|   if (first_ok < ptr) | ||||
|     { | ||||
|       g_string_append_len (op, first_ok, ptr - first_ok); | ||||
|     } | ||||
|   return g_string_free (op, FALSE); | ||||
| } | ||||
|  | ||||
| static char * | ||||
| get_escaped_dbus_path (const char *prefix, | ||||
|                        const char *component) | ||||
| { | ||||
|   char *escaped_component = escape_dbus_component (component); | ||||
|   char *path = g_strconcat (prefix, "/", component, NULL); | ||||
|  | ||||
|   g_free (escaped_component); | ||||
|   return path; | ||||
| } | ||||
|  | ||||
| static Login1Session * | ||||
| get_session_proxy (GCancellable *cancellable) | ||||
| { | ||||
|   char *proxy_path; | ||||
|   char *session_id; | ||||
|   Login1Session *session_proxy; | ||||
|  | ||||
|   if (sd_pid_get_session (getpid (), &session_id) < 0) | ||||
|     return NULL; | ||||
|  | ||||
|   proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id); | ||||
|  | ||||
|   session_proxy = login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, | ||||
|                                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, | ||||
|                                                          "org.freedesktop.login1", | ||||
|                                                          proxy_path, | ||||
|                                                          cancellable, NULL); | ||||
|   free (proxy_path); | ||||
|  | ||||
|   return session_proxy; | ||||
| } | ||||
|  | ||||
| static Login1Seat * | ||||
| get_seat_proxy (GCancellable *cancellable) | ||||
| { | ||||
|   return login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, | ||||
|                                              G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, | ||||
|                                              "org.freedesktop.login1", | ||||
|                                              "/org/freedesktop/login1/seat/self", | ||||
|                                              cancellable, NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| session_unpause (void) | ||||
| { | ||||
|   ClutterBackend *backend; | ||||
|   CoglContext *cogl_context; | ||||
|   CoglDisplay *cogl_display; | ||||
|  | ||||
|   backend = clutter_get_default_backend (); | ||||
|   cogl_context = clutter_backend_get_cogl_context (backend); | ||||
|   cogl_display = cogl_context_get_display (cogl_context); | ||||
|   cogl_kms_display_queue_modes_reset (cogl_display); | ||||
|  | ||||
|   clutter_set_paused (FALSE); | ||||
|   /* clutter_evdev_reclaim_devices (); */ | ||||
|  | ||||
|   { | ||||
|     MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); | ||||
|  | ||||
|     /* When we mode-switch back, we need to immediately queue a redraw | ||||
|      * in case nothing else quueed one for us, and force the cursor to | ||||
|      * update. */ | ||||
|  | ||||
|     clutter_actor_queue_redraw (compositor->stage); | ||||
|     meta_cursor_tracker_force_update (compositor->seat->cursor_tracker); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void | ||||
| session_pause (void) | ||||
| { | ||||
|   clutter_set_paused (TRUE); | ||||
|   /* clutter_evdev_release_devices (); */ | ||||
| } | ||||
|  | ||||
| static void | ||||
| sync_active (MetaLogin1 *self) | ||||
| { | ||||
|   gboolean active = login1_session_get_active (LOGIN1_SESSION (self->session_proxy)); | ||||
|  | ||||
|   if (active == self->session_active) | ||||
|     return; | ||||
|  | ||||
|   self->session_active = active; | ||||
|  | ||||
|   if (active) | ||||
|     session_unpause (); | ||||
|   else | ||||
|     session_pause (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| on_active_changed (Login1Session *session, | ||||
|                    GParamSpec    *pspec, | ||||
|                    gpointer       user_data) | ||||
| { | ||||
|   MetaLogin1 *self = user_data; | ||||
|   sync_active (self); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| take_device (Login1Session *session_proxy, | ||||
|              int            dev_major, | ||||
|              int            dev_minor, | ||||
|              int           *out_fd, | ||||
|              GCancellable  *cancellable, | ||||
|              GError       **error) | ||||
| { | ||||
|   gboolean ret = FALSE; | ||||
|   GVariant *fd_variant = NULL; | ||||
|   int fd = -1; | ||||
|   GUnixFDList *fd_list; | ||||
|  | ||||
|   if (!login1_session_call_take_device_sync (session_proxy, | ||||
|                                              dev_major, | ||||
|                                              dev_minor, | ||||
|                                              NULL, | ||||
|                                              &fd_variant, | ||||
|                                              NULL, /* paused */ | ||||
|                                              &fd_list, | ||||
|                                              cancellable, | ||||
|                                              error)) | ||||
|     goto out; | ||||
|  | ||||
|   fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error); | ||||
|   if (fd == -1) | ||||
|     goto out; | ||||
|  | ||||
|   *out_fd = fd; | ||||
|   ret = TRUE; | ||||
|  | ||||
|  out: | ||||
|   if (fd_variant) | ||||
|     g_variant_unref (fd_variant); | ||||
|   if (fd_list) | ||||
|     g_object_unref (fd_list); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| get_device_info_from_path (const char *path, | ||||
|                            int        *out_major, | ||||
|                            int        *out_minor) | ||||
| { | ||||
|   gboolean ret = FALSE; | ||||
|   int r; | ||||
|   struct stat st; | ||||
|  | ||||
|   r = stat (path, &st); | ||||
|   if (r < 0) | ||||
|     goto out; | ||||
|   if (!S_ISCHR (st.st_mode)) | ||||
|     goto out; | ||||
|  | ||||
|   *out_major = major (st.st_rdev); | ||||
|   *out_minor = minor (st.st_rdev); | ||||
|   ret = TRUE; | ||||
|  | ||||
|  out: | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| get_device_info_from_fd (int  fd, | ||||
|                          int *out_major, | ||||
|                          int *out_minor) | ||||
| { | ||||
|   gboolean ret = FALSE; | ||||
|   int r; | ||||
|   struct stat st; | ||||
|  | ||||
|   r = fstat (fd, &st); | ||||
|   if (r < 0) | ||||
|     goto out; | ||||
|   if (!S_ISCHR (st.st_mode)) | ||||
|     goto out; | ||||
|  | ||||
|   *out_major = major (st.st_rdev); | ||||
|   *out_minor = minor (st.st_rdev); | ||||
|   ret = TRUE; | ||||
|  | ||||
|  out: | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static int | ||||
| open_evdev_device (const char  *path, | ||||
|                    int          flags, | ||||
|                    gpointer     user_data, | ||||
|                    GError     **error) | ||||
| { | ||||
|   MetaLogin1 *self = user_data; | ||||
|   int fd; | ||||
|   int major, minor; | ||||
|  | ||||
|   if (!get_device_info_from_path (path, &major, &minor)) | ||||
|     { | ||||
|       g_set_error (error, | ||||
|                    G_IO_ERROR, | ||||
|                    G_IO_ERROR_NOT_FOUND, | ||||
|                    "Could not get device info for path %s: %m", path); | ||||
|       return -1; | ||||
|     } | ||||
|  | ||||
|   if (!take_device (self->session_proxy, major, minor, &fd, NULL, error)) | ||||
|     return -1; | ||||
|  | ||||
|   return fd; | ||||
| } | ||||
|  | ||||
| static void | ||||
| close_evdev_device (int      fd, | ||||
|                     gpointer user_data) | ||||
| { | ||||
|   MetaLogin1 *self = user_data; | ||||
|   int major, minor; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   if (!get_device_info_from_fd (fd, &major, &minor)) | ||||
|     { | ||||
|       g_warning ("Could not get device info for fd %d: %m", fd); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   if (!login1_session_call_release_device_sync (self->session_proxy, | ||||
|                                                 major, minor, | ||||
|                                                 NULL, &error)) | ||||
|     { | ||||
|       g_warning ("Could not release device %d,%d: %s", major, minor, error->message); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| get_kms_fd (Login1Session *session_proxy, | ||||
|             int *fd_out) | ||||
| { | ||||
|   int major, minor; | ||||
|   int fd; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   /* XXX -- use udev to find the DRM master device */ | ||||
|   if (!get_device_info_from_path ("/dev/dri/card0", &major, &minor)) | ||||
|     { | ||||
|       g_warning ("Could not stat /dev/dri/card0: %m"); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (!take_device (session_proxy, major, minor, &fd, NULL, &error)) | ||||
|     { | ||||
|       g_warning ("Could not open DRM device: %s\n", error->message); | ||||
|       g_error_free (error); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   *fd_out = fd; | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| MetaLogin1 * | ||||
| meta_login1_new (void) | ||||
| { | ||||
|   MetaLogin1 *self; | ||||
|   Login1Session *session_proxy; | ||||
|   GError *error = NULL; | ||||
|   int kms_fd; | ||||
|  | ||||
|   session_proxy = get_session_proxy (NULL); | ||||
|   if (!login1_session_call_take_control_sync (session_proxy, FALSE, NULL, &error)) | ||||
|     { | ||||
|       g_warning ("Could not take control: %s", error->message); | ||||
|       g_error_free (error); | ||||
|       return NULL; | ||||
|     } | ||||
|  | ||||
|   if (!get_kms_fd (session_proxy, &kms_fd)) | ||||
|     return NULL; | ||||
|  | ||||
|   self = g_slice_new0 (MetaLogin1); | ||||
|   self->session_proxy = session_proxy; | ||||
|   self->seat_proxy = get_seat_proxy (NULL); | ||||
|  | ||||
|   /* Clutter/Cogl start out in a state that assumes the session is active */ | ||||
|   self->session_active = TRUE; | ||||
|  | ||||
|   clutter_egl_set_kms_fd (kms_fd); | ||||
|   clutter_evdev_set_device_callbacks (open_evdev_device, | ||||
|                                       close_evdev_device, | ||||
|                                       self); | ||||
|  | ||||
|   g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self); | ||||
|   sync_active (self); | ||||
|  | ||||
|   return self; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_login1_free (MetaLogin1 *self) | ||||
| { | ||||
|   g_object_unref (self->seat_proxy); | ||||
|   g_object_unref (self->session_proxy); | ||||
|   g_slice_free (MetaLogin1, self); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_login1_activate_session (MetaLogin1  *self, | ||||
|                               GError     **error) | ||||
| { | ||||
|   if (!login1_session_call_activate_sync (self->session_proxy, NULL, error)) | ||||
|     return FALSE; | ||||
|  | ||||
|   sync_active (self); | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_login1_activate_vt (MetaLogin1  *self, | ||||
|                          int          vt, | ||||
|                          GError     **error) | ||||
| { | ||||
|   return login1_seat_call_switch_to_sync (self->seat_proxy, vt, NULL, error); | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2014 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #ifndef META_LOGIN1_H | ||||
| #define META_LOGIN1_H | ||||
|  | ||||
| #include <glib-object.h> | ||||
|  | ||||
| typedef struct _MetaLogin1 MetaLogin1; | ||||
|  | ||||
| MetaLogin1 *meta_login1_new              (void); | ||||
| void        meta_login1_free             (MetaLogin1  *self); | ||||
| gboolean    meta_login1_activate_session (MetaLogin1  *self, | ||||
|                                           GError     **error); | ||||
| gboolean    meta_login1_activate_vt      (MetaLogin1  *self, | ||||
|                                           int          vt, | ||||
|                                           GError     **error); | ||||
|  | ||||
| #endif | ||||
| @@ -48,7 +48,7 @@ data_offer_accept (struct wl_client *client, | ||||
|    * this be a wl_data_device request? */ | ||||
|  | ||||
|   if (offer->source) | ||||
|     wl_data_source_send_target (offer->source->resource, mime_type); | ||||
|     offer->source->accept (offer->source, serial, mime_type); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -58,9 +58,9 @@ data_offer_receive (struct wl_client *client, struct wl_resource *resource, | ||||
|   MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   if (offer->source) | ||||
|     wl_data_source_send_send (offer->source->resource, mime_type, fd); | ||||
|  | ||||
|   close (fd); | ||||
|     offer->source->send (offer->source, mime_type, fd); | ||||
|   else | ||||
|     close (fd); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -82,8 +82,7 @@ destroy_data_offer (struct wl_resource *resource) | ||||
|  | ||||
|   if (offer->source) | ||||
|     wl_list_remove (&offer->source_destroy_listener.link); | ||||
|  | ||||
|   g_slice_free (MetaWaylandDataOffer, offer); | ||||
|   free (offer); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -100,9 +99,13 @@ static struct wl_resource * | ||||
| meta_wayland_data_source_send_offer (MetaWaylandDataSource *source, | ||||
|                                      struct wl_resource *target) | ||||
| { | ||||
|   MetaWaylandDataOffer *offer = g_slice_new0 (MetaWaylandDataOffer); | ||||
|   MetaWaylandDataOffer *offer; | ||||
|   char **p; | ||||
|  | ||||
|   offer = malloc (sizeof *offer); | ||||
|   if (offer == NULL) | ||||
|     return NULL; | ||||
|  | ||||
|   offer->source = source; | ||||
|   offer->source_destroy_listener.notify = destroy_offer_data_source; | ||||
|  | ||||
| @@ -315,12 +318,8 @@ data_device_start_drag (struct wl_client *client, | ||||
| { | ||||
|   MetaWaylandSeat *seat = wl_resource_get_user_data (resource); | ||||
|   MetaWaylandDragGrab *drag_grab; | ||||
|  | ||||
|   if ((seat->pointer.button_count == 0 || | ||||
|        seat->pointer.grab_serial != serial || | ||||
|        !seat->pointer.focus_surface || | ||||
|        seat->pointer.focus_surface != wl_resource_get_user_data (origin_resource))) | ||||
|     return; | ||||
|   /* FIXME: Check that client has implicit grab on the origin | ||||
|    * surface that matches the given time. */ | ||||
|  | ||||
|   /* FIXME: Check that the data source type array isn't empty. */ | ||||
|  | ||||
| @@ -333,7 +332,6 @@ data_device_start_drag (struct wl_client *client, | ||||
|   drag_grab->generic.pointer = &seat->pointer; | ||||
|  | ||||
|   drag_grab->drag_client = client; | ||||
|   drag_grab->seat = seat; | ||||
|  | ||||
|   if (source_resource) | ||||
|     { | ||||
| @@ -377,7 +375,7 @@ destroy_selection_data_source (struct wl_listener *listener, void *data) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| void | ||||
| meta_wayland_seat_set_selection (MetaWaylandSeat *seat, | ||||
|                                  MetaWaylandDataSource *source, | ||||
|                                  guint32 serial) | ||||
| @@ -391,7 +389,7 @@ meta_wayland_seat_set_selection (MetaWaylandSeat *seat, | ||||
|  | ||||
|   if (seat->selection_data_source) | ||||
|     { | ||||
|       wl_data_source_send_cancelled (seat->selection_data_source->resource); | ||||
|       seat->selection_data_source->cancel (seat->selection_data_source); | ||||
|       wl_list_remove (&seat->selection_data_source_listener.link); | ||||
|       seat->selection_data_source = NULL; | ||||
|     } | ||||
| @@ -451,21 +449,47 @@ static const struct wl_data_device_interface data_device_interface = { | ||||
| static void | ||||
| destroy_data_source (struct wl_resource *resource) | ||||
| { | ||||
|   MetaWaylandDataSource *source = wl_resource_get_user_data (resource); | ||||
|   MetaWaylandDataSource *source = wl_container_of (resource, source, resource); | ||||
|   char **p; | ||||
|  | ||||
|   wl_array_for_each (p, &source->mime_types) free (*p); | ||||
|  | ||||
|   wl_array_release (&source->mime_types); | ||||
| } | ||||
|  | ||||
|   g_slice_free (MetaWaylandDataSource, source); | ||||
| static void | ||||
| client_source_accept (MetaWaylandDataSource *source, | ||||
|                       guint32 time, const char *mime_type) | ||||
| { | ||||
|   wl_data_source_send_target (source->resource, mime_type); | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_source_send (MetaWaylandDataSource *source, | ||||
|                     const char *mime_type, int32_t fd) | ||||
| { | ||||
|   wl_data_source_send_send (source->resource, mime_type, fd); | ||||
|   close (fd); | ||||
| } | ||||
|  | ||||
| static void | ||||
| client_source_cancel (MetaWaylandDataSource *source) | ||||
| { | ||||
|   wl_data_source_send_cancelled (source->resource); | ||||
| } | ||||
|  | ||||
| static void | ||||
| create_data_source (struct wl_client *client, | ||||
|                     struct wl_resource *resource, guint32 id) | ||||
| { | ||||
|   MetaWaylandDataSource *source = g_slice_new0 (MetaWaylandDataSource); | ||||
|   MetaWaylandDataSource *source; | ||||
|  | ||||
|   source = malloc (sizeof *source); | ||||
|   if (source == NULL) | ||||
|     { | ||||
|       wl_resource_post_no_memory (resource); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   source->resource = wl_resource_create (client, &wl_data_source_interface, | ||||
| 					 MIN (META_WL_DATA_SOURCE_VERSION, | ||||
| @@ -473,6 +497,10 @@ create_data_source (struct wl_client *client, | ||||
|   wl_resource_set_implementation (source->resource, &data_source_interface, | ||||
| 				  source, destroy_data_source); | ||||
|  | ||||
|   source->accept = client_source_accept; | ||||
|   source->send = client_source_send; | ||||
|   source->cancel = client_source_cancel; | ||||
|  | ||||
|   wl_array_init (&source->mime_types); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,4 +33,10 @@ meta_wayland_data_device_set_keyboard_focus (MetaWaylandSeat *seat); | ||||
| int | ||||
| meta_wayland_data_device_manager_init (struct wl_display *display); | ||||
|  | ||||
| void | ||||
| meta_wayland_seat_set_selection (MetaWaylandSeat *seat, | ||||
|                                  MetaWaylandDataSource *source, | ||||
|                                  uint32_t serial); | ||||
|  | ||||
|  | ||||
| #endif /* __META_WAYLAND_DATA_DEVICE_H__ */ | ||||
|   | ||||
| @@ -142,6 +142,20 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, | ||||
|     xkb_keymap_unref (xkb_info->keymap); | ||||
|   xkb_info->keymap = keymap; | ||||
|  | ||||
|   xkb_info->shift_mod = | ||||
|     xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_SHIFT); | ||||
|   xkb_info->caps_mod = | ||||
|     xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CAPS); | ||||
|   xkb_info->ctrl_mod = | ||||
|     xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CTRL); | ||||
|   xkb_info->alt_mod = | ||||
|     xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_ALT); | ||||
|   xkb_info->mod2_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod2"); | ||||
|   xkb_info->mod3_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod3"); | ||||
|   xkb_info->super_mod = | ||||
|     xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_LOGO); | ||||
|   xkb_info->mod5_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod5"); | ||||
|  | ||||
|   keymap_str = xkb_map_get_as_string (xkb_info->keymap); | ||||
|   if (keymap_str == NULL) | ||||
|     { | ||||
| @@ -281,6 +295,31 @@ static const MetaWaylandKeyboardGrabInterface | ||||
|   default_grab_modifiers, | ||||
| }; | ||||
|  | ||||
| static gboolean | ||||
| modal_key (MetaWaylandKeyboardGrab *grab, | ||||
| 	   uint32_t                 time, | ||||
| 	   uint32_t                 key, | ||||
| 	   uint32_t                 state) | ||||
| { | ||||
|   /* FALSE means: let the event through to clutter */ | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| modal_modifiers (MetaWaylandKeyboardGrab *grab, | ||||
| 		 uint32_t                 serial, | ||||
| 		 uint32_t                 mods_depressed, | ||||
| 		 uint32_t                 mods_latched, | ||||
| 		 uint32_t                 mods_locked, | ||||
| 		 uint32_t                 group) | ||||
| { | ||||
| } | ||||
|  | ||||
| static MetaWaylandKeyboardGrabInterface modal_grab = { | ||||
|   modal_key, | ||||
|   modal_modifiers, | ||||
| }; | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard, | ||||
|                             struct wl_display   *display) | ||||
| @@ -496,13 +535,26 @@ meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard, | ||||
|           struct wl_display *display = wl_client_get_display (client); | ||||
|           uint32_t serial = wl_display_next_serial (display); | ||||
|  | ||||
|           wl_keyboard_send_modifiers (keyboard->focus_resource, serial, | ||||
|                                       keyboard->modifier_state.mods_depressed, | ||||
|                                       keyboard->modifier_state.mods_latched, | ||||
|                                       keyboard->modifier_state.mods_locked, | ||||
|                                       keyboard->modifier_state.group); | ||||
|           wl_keyboard_send_enter (keyboard->focus_resource, serial, keyboard->focus_surface->resource, | ||||
|                                   &keyboard->keys); | ||||
|           /* If we're in a modal grab, the client is focused but doesn't see | ||||
|              modifiers or pressed keys (and fix that up when we exit the modal) */ | ||||
|           if (keyboard->grab->interface == &modal_grab) | ||||
|             { | ||||
|               struct wl_array empty; | ||||
|               wl_array_init (&empty); | ||||
|  | ||||
|               wl_keyboard_send_modifiers (keyboard->focus_resource, serial, 0, 0, 0, 0); | ||||
|               wl_keyboard_send_enter (keyboard->focus_resource, serial, keyboard->focus_surface->resource, &empty); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               wl_keyboard_send_modifiers (keyboard->focus_resource, serial, | ||||
|                                           keyboard->modifier_state.mods_depressed, | ||||
|                                           keyboard->modifier_state.mods_latched, | ||||
|                                           keyboard->modifier_state.mods_locked, | ||||
|                                           keyboard->modifier_state.group); | ||||
|               wl_keyboard_send_enter (keyboard->focus_resource, serial, keyboard->focus_surface->resource, | ||||
|                                       &keyboard->keys); | ||||
|             } | ||||
|  | ||||
|           wl_resource_add_destroy_listener (keyboard->focus_resource, &keyboard->focus_resource_listener); | ||||
|           keyboard->focus_serial = serial; | ||||
| @@ -536,6 +588,85 @@ meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard) | ||||
|   wl_array_release (&keyboard->keys); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard, | ||||
| 				   guint32              timestamp) | ||||
| { | ||||
|   MetaWaylandKeyboardGrab *grab; | ||||
|   uint32_t *end = (void *) ((char *) keyboard->keys.data + | ||||
| 			    keyboard->keys.size); | ||||
|   uint32_t *k; | ||||
|   uint32_t serial; | ||||
|  | ||||
|   meta_verbose ("Asked to acquire modal keyboard grab, timestamp %d\n", timestamp); | ||||
|  | ||||
|   if (keyboard->grab != &keyboard->default_grab) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (keyboard->focus_surface) | ||||
|     { | ||||
|       /* Fake key release events for the focused app */ | ||||
|       serial = wl_display_next_serial (keyboard->display); | ||||
|       keyboard->grab->interface->modifiers (keyboard->grab, | ||||
| 					    serial, | ||||
| 					    0, 0, 0, 0); | ||||
|  | ||||
|       for (k = keyboard->keys.data; k < end; k++) | ||||
| 	{ | ||||
| 	  keyboard->grab->interface->key (keyboard->grab, | ||||
| 					  timestamp, | ||||
| 					  *k, 0); | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|   grab = g_slice_new0 (MetaWaylandKeyboardGrab); | ||||
|   grab->interface = &modal_grab; | ||||
|   meta_wayland_keyboard_start_grab (keyboard, grab); | ||||
|  | ||||
|   meta_verbose ("Acquired modal keyboard grab, timestamp %d\n", timestamp); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard, | ||||
| 				 guint32              timestamp) | ||||
| { | ||||
|   MetaWaylandKeyboardGrab *grab; | ||||
|   uint32_t *end = (void *) ((char *) keyboard->keys.data + | ||||
| 			    keyboard->keys.size); | ||||
|   uint32_t *k; | ||||
|   uint32_t serial; | ||||
|  | ||||
|   grab = keyboard->grab; | ||||
|  | ||||
|   g_assert (grab->interface == &modal_grab); | ||||
|  | ||||
|   meta_wayland_keyboard_end_grab (keyboard); | ||||
|   g_slice_free (MetaWaylandKeyboardGrab, grab); | ||||
|  | ||||
|   if (keyboard->focus_surface) | ||||
|     { | ||||
|       /* Fake key press events for the focused app */ | ||||
|       serial = wl_display_next_serial (keyboard->display); | ||||
|       keyboard->grab->interface->modifiers (keyboard->grab, | ||||
| 					    serial, | ||||
| 					    keyboard->modifier_state.mods_depressed, | ||||
| 					    keyboard->modifier_state.mods_latched,  | ||||
| 					    keyboard->modifier_state.mods_locked, | ||||
| 					    keyboard->modifier_state.group); | ||||
|  | ||||
|       for (k = keyboard->keys.data; k < end; k++) | ||||
| 	{ | ||||
| 	  keyboard->grab->interface->key (keyboard->grab, | ||||
| 					  timestamp, | ||||
| 					  *k, 1); | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|   meta_verbose ("Released modal keyboard grab, timestamp %d\n", timestamp); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_keyboard_set_keymap_names (MetaWaylandKeyboard *keyboard, | ||||
| 					const char          *rules, | ||||
|   | ||||
| @@ -72,6 +72,14 @@ typedef struct | ||||
|   int keymap_fd; | ||||
|   size_t keymap_size; | ||||
|   char *keymap_area; | ||||
|   xkb_mod_index_t shift_mod; | ||||
|   xkb_mod_index_t caps_mod; | ||||
|   xkb_mod_index_t ctrl_mod; | ||||
|   xkb_mod_index_t alt_mod; | ||||
|   xkb_mod_index_t mod2_mod; | ||||
|   xkb_mod_index_t mod3_mod; | ||||
|   xkb_mod_index_t super_mod; | ||||
|   xkb_mod_index_t mod5_mod; | ||||
| } MetaWaylandXkbInfo; | ||||
|  | ||||
| typedef struct | ||||
| @@ -142,6 +150,13 @@ meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device, | ||||
| void | ||||
| meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard); | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard, | ||||
| 				   guint32              timestamp); | ||||
| void | ||||
| meta_wayland_keyboard_end_modal   (MetaWaylandKeyboard *keyboard, | ||||
| 				   guint32              timestamp); | ||||
|  | ||||
| void | ||||
| meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard); | ||||
|  | ||||
|   | ||||
| @@ -398,6 +398,61 @@ meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer) | ||||
|   interface->focus (pointer->grab, pointer->current, NULL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| modal_focus (MetaWaylandPointerGrab *grab, | ||||
| 	     MetaWaylandSurface     *surface, | ||||
| 	     const ClutterEvent     *event) | ||||
| { | ||||
| } | ||||
|  | ||||
| static void | ||||
| modal_motion (MetaWaylandPointerGrab *grab, | ||||
| 	      const ClutterEvent     *event) | ||||
| { | ||||
| } | ||||
|  | ||||
| static void | ||||
| modal_button (MetaWaylandPointerGrab *grab, | ||||
| 	      const ClutterEvent     *event) | ||||
| { | ||||
| } | ||||
|  | ||||
| static MetaWaylandPointerGrabInterface modal_grab = { | ||||
|   modal_focus, | ||||
|   modal_motion, | ||||
|   modal_button | ||||
| }; | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer) | ||||
| { | ||||
|   MetaWaylandPointerGrab *grab; | ||||
|  | ||||
|   if (pointer->grab != &pointer->default_grab) | ||||
|     return FALSE; | ||||
|  | ||||
|   meta_wayland_pointer_set_focus (pointer, NULL); | ||||
|  | ||||
|   grab = g_slice_new0 (MetaWaylandPointerGrab); | ||||
|   grab->interface = &modal_grab; | ||||
|   meta_wayland_pointer_start_grab (pointer, grab); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_pointer_end_modal (MetaWaylandPointer *pointer) | ||||
| { | ||||
|   MetaWaylandPointerGrab *grab; | ||||
|  | ||||
|   grab = pointer->grab; | ||||
|  | ||||
|   g_assert (grab->interface == &modal_grab); | ||||
|  | ||||
|   meta_wayland_pointer_end_grab (pointer); | ||||
|   g_slice_free (MetaWaylandPointerGrab, grab); | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|   MetaWaylandPointerGrab  generic; | ||||
|  | ||||
| @@ -478,12 +533,6 @@ meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer) | ||||
|       g_slice_free (MetaWaylandPopup, popup); | ||||
|     } | ||||
|  | ||||
|   { | ||||
|     MetaDisplay *display = meta_get_display (); | ||||
|     meta_display_end_grab_op (display, | ||||
|                               meta_display_get_current_time_roundtrip (display)); | ||||
|   } | ||||
|  | ||||
|   meta_wayland_pointer_end_grab (pointer); | ||||
|   g_slice_free (MetaWaylandPopupGrab, popup_grab); | ||||
| } | ||||
| @@ -523,8 +572,6 @@ meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, | ||||
|  | ||||
|   if (pointer->grab == &pointer->default_grab) | ||||
|     { | ||||
|       MetaWindow *window = surface->window; | ||||
|  | ||||
|       grab = g_slice_new0 (MetaWaylandPopupGrab); | ||||
|       grab->generic.interface = &popup_grab_interface; | ||||
|       grab->generic.pointer = pointer; | ||||
| @@ -532,19 +579,6 @@ meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, | ||||
|       wl_list_init (&grab->all_popups); | ||||
|  | ||||
|       meta_wayland_pointer_start_grab (pointer, (MetaWaylandPointerGrab*)grab); | ||||
|  | ||||
|       meta_display_begin_grab_op (window->display, | ||||
|                                   window->screen, | ||||
|                                   window, | ||||
|                                   META_GRAB_OP_WAYLAND_CLIENT, | ||||
|                                   FALSE, /* pointer_already_grabbed */ | ||||
|                                   FALSE, /* frame_action */ | ||||
|                                   1, /* button. XXX? */ | ||||
|                                   0, /* modmask */ | ||||
|                                   meta_display_get_current_time_roundtrip (window->display), | ||||
|                                   wl_fixed_to_int (pointer->grab_x), | ||||
|                                   wl_fixed_to_int (pointer->grab_y)); | ||||
|  | ||||
|     } | ||||
|   else | ||||
|     grab = (MetaWaylandPopupGrab*)pointer->grab; | ||||
| @@ -559,6 +593,7 @@ meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, | ||||
|     wl_resource_add_destroy_listener (surface->wl_shell_surface.resource, &popup->surface_destroy_listener); | ||||
|  | ||||
|   wl_list_insert (&grab->all_popups, &popup->link); | ||||
|        | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -85,6 +85,11 @@ meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, | ||||
| void | ||||
| meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer); | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_pointer_begin_modal (MetaWaylandPointer *pointer); | ||||
| void | ||||
| meta_wayland_pointer_end_modal   (MetaWaylandPointer *pointer); | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, | ||||
| 				       MetaWaylandSurface *popup); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
| #include <cairo.h> | ||||
|  | ||||
| #include "window-private.h" | ||||
| #include "meta-login1.h" | ||||
| #include "meta-weston-launch.h" | ||||
| #include <meta/meta-cursor-tracker.h> | ||||
|  | ||||
| #include "meta-wayland.h" | ||||
| @@ -85,7 +85,7 @@ struct _MetaWaylandCompositor | ||||
|  | ||||
|   MetaXWaylandManager xwayland_manager; | ||||
|  | ||||
|   MetaLogin1 *login1; | ||||
|   MetaLauncher *launcher; | ||||
|  | ||||
|   MetaWaylandSeat *seat; | ||||
| }; | ||||
|   | ||||
| @@ -276,53 +276,42 @@ static void | ||||
| handle_scroll_event (MetaWaylandSeat    *seat, | ||||
|                      const ClutterEvent *event) | ||||
| { | ||||
|   wl_fixed_t x_value = 0, y_value = 0; | ||||
|   enum wl_pointer_axis axis; | ||||
|   wl_fixed_t value; | ||||
|  | ||||
|   notify_motion (seat, event); | ||||
|  | ||||
|   if (!seat->pointer.focus_resource) | ||||
|     return; | ||||
|  | ||||
|   if (clutter_event_is_pointer_emulated (event)) | ||||
|     return; | ||||
|  | ||||
|   switch (clutter_event_get_scroll_direction (event)) | ||||
|     { | ||||
|     case CLUTTER_SCROLL_UP: | ||||
|       y_value = -DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       axis = WL_POINTER_AXIS_VERTICAL_SCROLL; | ||||
|       value = -DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       break; | ||||
|  | ||||
|     case CLUTTER_SCROLL_DOWN: | ||||
|       y_value = DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       axis = WL_POINTER_AXIS_VERTICAL_SCROLL; | ||||
|       value = DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       break; | ||||
|  | ||||
|     case CLUTTER_SCROLL_LEFT: | ||||
|       x_value = -DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL; | ||||
|       value = -DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       break; | ||||
|  | ||||
|     case CLUTTER_SCROLL_RIGHT: | ||||
|       x_value = DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       break; | ||||
|  | ||||
|     case CLUTTER_SCROLL_SMOOTH: | ||||
|       { | ||||
|         double dx, dy; | ||||
|         clutter_event_get_scroll_delta (event, &dx, &dy); | ||||
|         x_value = wl_fixed_from_double (dx); | ||||
|         y_value = wl_fixed_from_double (dy); | ||||
|       } | ||||
|       axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL; | ||||
|       value = DEFAULT_AXIS_STEP_DISTANCE; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   if (x_value) | ||||
|     wl_pointer_send_axis (seat->pointer.focus_resource, clutter_event_get_time (event), | ||||
|                           WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_value); | ||||
|   if (y_value) | ||||
|     wl_pointer_send_axis (seat->pointer.focus_resource, clutter_event_get_time (event), | ||||
|                           WL_POINTER_AXIS_VERTICAL_SCROLL, y_value); | ||||
|   if (seat->pointer.focus_resource) | ||||
|     wl_pointer_send_axis (seat->pointer.focus_resource, | ||||
|                           clutter_event_get_time (event), | ||||
|                           axis, | ||||
|                           value); | ||||
| } | ||||
|  | ||||
| static int | ||||
|   | ||||
| @@ -43,6 +43,12 @@ struct _MetaWaylandDataSource | ||||
| { | ||||
|   struct wl_resource *resource; | ||||
|   struct wl_array mime_types; | ||||
|  | ||||
|   void (*accept) (MetaWaylandDataSource * source, | ||||
|                   uint32_t serial, const char *mime_type); | ||||
|   void (*send) (MetaWaylandDataSource * source, | ||||
|                 const char *mime_type, int32_t fd); | ||||
|   void (*cancel) (MetaWaylandDataSource * source); | ||||
| }; | ||||
|  | ||||
| struct _MetaWaylandSeat | ||||
|   | ||||
| @@ -336,6 +336,22 @@ toplevel_surface_commit (MetaWaylandSurface             *surface, | ||||
|  | ||||
|   if (pending->frame_extents_changed) | ||||
|     meta_window_set_custom_frame_extents (surface->window, &pending->frame_extents); | ||||
|  | ||||
|   if (pending->maximized.changed) | ||||
|     { | ||||
|       if (pending->maximized.value) | ||||
|         meta_window_maximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
|       else | ||||
|         meta_window_unmaximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); | ||||
|     } | ||||
|  | ||||
|   if (pending->fullscreen.changed) | ||||
|     { | ||||
|       if (pending->fullscreen.value) | ||||
|         meta_window_make_fullscreen (surface->window); | ||||
|       else | ||||
|         meta_window_unmake_fullscreen (surface->window); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -361,6 +377,8 @@ double_buffered_state_init (MetaWaylandDoubleBufferedState *state) | ||||
|   wl_list_init (&state->frame_callback_list); | ||||
|  | ||||
|   state->frame_extents_changed = FALSE; | ||||
|   state->maximized.changed = FALSE; | ||||
|   state->fullscreen.changed = FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -749,49 +767,6 @@ xdg_surface_set_app_id (struct wl_client *client, | ||||
|   meta_window_set_wm_class (surface->window, app_id, app_id); | ||||
| } | ||||
|  | ||||
| static MetaWindowType | ||||
| window_type_from_surface_type (uint32_t surface_type) | ||||
| { | ||||
|   switch (surface_type) | ||||
|     { | ||||
|     case XDG_SURFACE_SURFACE_TYPE_NORMAL: | ||||
|       return META_WINDOW_NORMAL; | ||||
|     case XDG_SURFACE_SURFACE_TYPE_MODAL_DIALOG: | ||||
|       return META_WINDOW_MODAL_DIALOG; | ||||
|     default: | ||||
|       g_warning ("Unknown surface type: %d\n", surface_type); | ||||
|       return META_WINDOW_NORMAL; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_set_surface_type (struct wl_client *client, | ||||
|                               struct wl_resource *resource, | ||||
|                               uint32_t surface_type) | ||||
| { | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   meta_window_set_type (surface->window, | ||||
|                         window_type_from_surface_type (surface_type)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_show_window_menu (struct wl_client *client, | ||||
|                               struct wl_resource *resource, | ||||
|                               struct wl_resource *seat_resource, | ||||
|                               uint32_t serial) | ||||
| { | ||||
|   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   if (seat->pointer.button_count == 0 || | ||||
|       seat->pointer.grab_serial != serial || | ||||
|       seat->pointer.focus_surface != surface) | ||||
|     return; | ||||
|  | ||||
|   meta_window_show_menu (surface->window); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| begin_grab_op_on_surface (MetaWaylandSurface *surface, | ||||
|                           MetaWaylandSeat    *seat, | ||||
| @@ -886,44 +861,44 @@ xdg_surface_set_output (struct wl_client *client, | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_request_change_state (struct wl_client *client, | ||||
|                                   struct wl_resource *resource, | ||||
|                                   uint32_t state_type, | ||||
|                                   uint32_t value, | ||||
|                                   uint32_t serial) | ||||
| xdg_surface_set_fullscreen (struct wl_client *client, | ||||
|                             struct wl_resource *resource) | ||||
| { | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   surface->state_changed_serial = serial; | ||||
|  | ||||
|   switch (state_type) | ||||
|     { | ||||
|     case XDG_SURFACE_STATE_MAXIMIZED: | ||||
|       if (value) | ||||
|         meta_window_maximize (surface->window, META_MAXIMIZE_BOTH); | ||||
|       else | ||||
|         meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH); | ||||
|       break; | ||||
|     case XDG_SURFACE_STATE_FULLSCREEN: | ||||
|       if (value) | ||||
|         meta_window_make_fullscreen (surface->window); | ||||
|       else | ||||
|         meta_window_unmake_fullscreen (surface->window); | ||||
|     } | ||||
|   surface->pending.fullscreen.changed = TRUE; | ||||
|   surface->pending.fullscreen.value = TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_ack_change_state (struct wl_client *client, | ||||
|                               struct wl_resource *resource, | ||||
|                               uint32_t state_type, | ||||
|                               uint32_t value, | ||||
|                               uint32_t serial) | ||||
| xdg_surface_unset_fullscreen (struct wl_client *client, | ||||
|                               struct wl_resource *resource) | ||||
| { | ||||
|   /* Do nothing for now. In the future, we'd imagine that | ||||
|    * we'd ignore attaches when we have a state pending that | ||||
|    * we haven't had the client ACK'd, to prevent a race | ||||
|    * condition when we have an in-flight attach when the | ||||
|    * client gets the new state. */ | ||||
|   MetaWaylandSurfaceExtension *xdg_surface = wl_resource_get_user_data (resource); | ||||
|   MetaWaylandSurface *surface = wl_container_of (xdg_surface, surface, xdg_surface); | ||||
|  | ||||
|   surface->pending.fullscreen.changed = TRUE; | ||||
|   surface->pending.fullscreen.value = FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_set_maximized (struct wl_client *client, | ||||
|                            struct wl_resource *resource) | ||||
| { | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   surface->pending.maximized.changed = TRUE; | ||||
|   surface->pending.maximized.value = TRUE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| xdg_surface_unset_maximized (struct wl_client *client, | ||||
|                              struct wl_resource *resource) | ||||
| { | ||||
|   MetaWaylandSurface *surface = wl_resource_get_user_data (resource); | ||||
|  | ||||
|   surface->pending.maximized.changed = TRUE; | ||||
|   surface->pending.maximized.value = FALSE; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -941,13 +916,13 @@ static const struct xdg_surface_interface meta_wayland_xdg_surface_interface = { | ||||
|   xdg_surface_set_margin, | ||||
|   xdg_surface_set_title, | ||||
|   xdg_surface_set_app_id, | ||||
|   xdg_surface_set_surface_type, | ||||
|   xdg_surface_show_window_menu, | ||||
|   xdg_surface_move, | ||||
|   xdg_surface_resize, | ||||
|   xdg_surface_set_output, | ||||
|   xdg_surface_request_change_state, | ||||
|   xdg_surface_ack_change_state, | ||||
|   xdg_surface_set_fullscreen, | ||||
|   xdg_surface_unset_fullscreen, | ||||
|   xdg_surface_set_maximized, | ||||
|   xdg_surface_unset_maximized, | ||||
|   xdg_surface_set_minimized, | ||||
| }; | ||||
|  | ||||
| @@ -1036,7 +1011,8 @@ xdg_shell_get_xdg_popup (struct wl_client *client, | ||||
|   surface->window->placed = TRUE; | ||||
|   meta_window_set_transient_for (surface->window, parent_surf->window); | ||||
|  | ||||
|   meta_window_set_type (surface->window, META_WINDOW_DROPDOWN_MENU); | ||||
|   surface->window->type = META_WINDOW_DROPDOWN_MENU; | ||||
|   meta_window_type_changed (surface->window); | ||||
|  | ||||
|   meta_wayland_pointer_start_popup_grab (&seat->pointer, surface); | ||||
| } | ||||
| @@ -1198,9 +1174,9 @@ wl_shell_surface_set_state (MetaWaylandSurface *surface, | ||||
|     meta_window_unmake_fullscreen (surface->window); | ||||
|  | ||||
|   if (state == SURFACE_STATE_MAXIMIZED) | ||||
|     meta_window_maximize (surface->window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_maximize (surface->window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL); | ||||
|   else | ||||
|     meta_window_unmaximize (surface->window, META_MAXIMIZE_BOTH); | ||||
|     meta_window_unmaximize (surface->window, META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1489,6 +1465,8 @@ subsurface_parent_surface_committed (MetaWaylandSurface *surface) | ||||
|  | ||||
|   if (surface->sub.synchronous) | ||||
|     commit_double_buffered_state (surface, pending_surface_state); | ||||
|  | ||||
|   double_buffered_state_reset (pending_surface_state); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -1760,55 +1738,6 @@ meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, | ||||
|                                      0, new_width, new_height); | ||||
| } | ||||
|  | ||||
| static void | ||||
| send_change_state (MetaWaylandSurface *surface, | ||||
|                    uint32_t state_type, | ||||
|                    uint32_t value) | ||||
| { | ||||
|   if (surface->xdg_surface.resource) | ||||
|     { | ||||
|       uint32_t serial; | ||||
|  | ||||
|       if (surface->state_changed_serial != 0) | ||||
|         { | ||||
|           serial = surface->state_changed_serial; | ||||
|           surface->state_changed_serial = 0; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           struct wl_client *client = wl_resource_get_client (surface->xdg_surface.resource); | ||||
|           struct wl_display *display = wl_client_get_display (client); | ||||
|           serial = wl_display_next_serial (display); | ||||
|         } | ||||
|  | ||||
|       xdg_surface_send_change_state (surface->xdg_surface.resource, state_type, value, serial); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_surface_send_maximized (MetaWaylandSurface *surface) | ||||
| { | ||||
|   send_change_state (surface, XDG_SURFACE_STATE_MAXIMIZED, TRUE); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_surface_send_unmaximized (MetaWaylandSurface *surface) | ||||
| { | ||||
|   send_change_state (surface, XDG_SURFACE_STATE_MAXIMIZED, FALSE); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_surface_send_fullscreened (MetaWaylandSurface *surface) | ||||
| { | ||||
|   send_change_state (surface, XDG_SURFACE_STATE_FULLSCREEN, TRUE); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_surface_send_unfullscreened (MetaWaylandSurface *surface) | ||||
| { | ||||
|   send_change_state (surface, XDG_SURFACE_STATE_FULLSCREEN, FALSE); | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_wayland_surface_activated (MetaWaylandSurface *surface) | ||||
| { | ||||
|   | ||||
| @@ -41,6 +41,12 @@ struct _MetaWaylandBuffer | ||||
|   uint32_t ref_count; | ||||
| }; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   guint changed : 1; | ||||
|   guint value : 1; | ||||
| } MetaWaylandStateFlag; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   /* wl_surface.attach */ | ||||
| @@ -61,6 +67,9 @@ typedef struct | ||||
|  | ||||
|   gboolean frame_extents_changed; | ||||
|   GtkBorder frame_extents; | ||||
|  | ||||
|   MetaWaylandStateFlag fullscreen; | ||||
|   MetaWaylandStateFlag maximized; | ||||
| } MetaWaylandDoubleBufferedState; | ||||
|  | ||||
| typedef struct | ||||
| @@ -98,8 +107,6 @@ struct _MetaWaylandSurface | ||||
|     GSList *pending_placement_ops; | ||||
|   } sub; | ||||
|  | ||||
|   uint32_t state_changed_serial; | ||||
|  | ||||
|   /* All the pending state, that wl_surface.commit will apply. */ | ||||
|   MetaWaylandDoubleBufferedState pending; | ||||
| }; | ||||
| @@ -117,10 +124,6 @@ void                meta_wayland_surface_window_unmanaged (MetaWaylandSurface *s | ||||
| void                meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, | ||||
| 							   int                 width, | ||||
| 							   int                 height); | ||||
| void                meta_wayland_surface_send_maximized (MetaWaylandSurface *surface); | ||||
| void                meta_wayland_surface_send_unmaximized (MetaWaylandSurface *surface); | ||||
| void                meta_wayland_surface_send_fullscreened (MetaWaylandSurface *surface); | ||||
| void                meta_wayland_surface_send_unfullscreened (MetaWaylandSurface *surface); | ||||
|  | ||||
| void                meta_wayland_surface_activated (MetaWaylandSurface *surface); | ||||
| void                meta_wayland_surface_deactivated (MetaWaylandSurface *surface); | ||||
|   | ||||
| @@ -52,7 +52,7 @@ | ||||
| #include <meta/main.h> | ||||
| #include "frame.h" | ||||
| #include "meta-idle-monitor-private.h" | ||||
| #include "meta-login1.h" | ||||
| #include "meta-weston-launch.h" | ||||
| #include "monitor-private.h" | ||||
|  | ||||
| static MetaWaylandCompositor _meta_wayland_compositor; | ||||
| @@ -643,16 +643,25 @@ meta_wayland_init (void) | ||||
|  | ||||
|   clutter_wayland_set_compositor_display (compositor->wayland_display); | ||||
|  | ||||
|   /* If we're running on bare metal, we're a display server, | ||||
|    * so start talking to logind. */ | ||||
| #if defined(CLUTTER_WINDOWING_EGL) | ||||
|   if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL)) | ||||
|     compositor->login1 = meta_login1_new (); | ||||
|     compositor->launcher = meta_launcher_new (); | ||||
| #endif | ||||
|  | ||||
|   if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) | ||||
|     g_error ("Failed to initialize Clutter"); | ||||
|  | ||||
| #if defined(CLUTTER_WINDOWING_EGL) | ||||
|   if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL)) | ||||
|     { | ||||
|       ClutterBackend *backend = clutter_get_default_backend (); | ||||
|       CoglContext *cogl_context = clutter_backend_get_cogl_context (backend); | ||||
|       CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context)); | ||||
|       int drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer); | ||||
|       meta_launcher_set_drm_fd (compositor->launcher, drm_fd, NULL); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|   meta_monitor_manager_initialize (); | ||||
|   monitors = meta_monitor_manager_get (); | ||||
|   g_signal_connect (monitors, "monitors-changed", | ||||
| @@ -704,8 +713,8 @@ meta_wayland_finalize (void) | ||||
|  | ||||
|   meta_xwayland_stop (&compositor->xwayland_manager); | ||||
|  | ||||
|   if (compositor->login1) | ||||
|     meta_login1_free (compositor->login1); | ||||
|   if (compositor->launcher) | ||||
|     meta_launcher_free (compositor->launcher); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| @@ -713,28 +722,13 @@ meta_wayland_compositor_activate_vt (MetaWaylandCompositor  *compositor, | ||||
|                                      int                     vt, | ||||
|                                      GError                **error) | ||||
| { | ||||
|   if (compositor->login1) | ||||
|   if (compositor->launcher) | ||||
|     { | ||||
|       return meta_login1_activate_vt (compositor->login1, vt, error); | ||||
|       return meta_launcher_activate_vt (compositor->launcher, vt, error); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       g_debug ("Ignoring VT switch keybinding, not running as display server"); | ||||
|       return TRUE; | ||||
|     } | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_wayland_compositor_activate_session (MetaWaylandCompositor  *compositor, | ||||
|                                           GError                **error) | ||||
| { | ||||
|   if (compositor->login1) | ||||
|     { | ||||
|       return meta_login1_activate_session (compositor->login1, error); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       g_debug ("Ignoring activate_session, not running as display server"); | ||||
|       g_debug ("Ignoring VT switch keybinding, not running as VT manager"); | ||||
|       return TRUE; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -46,8 +46,6 @@ void                    meta_wayland_compositor_paint_finished  (MetaWaylandComp | ||||
| gboolean                meta_wayland_compositor_activate_vt     (MetaWaylandCompositor  *compositor, | ||||
|                                                                  int                     vt, | ||||
|                                                                  GError                **error); | ||||
| gboolean                meta_wayland_compositor_activate_session (MetaWaylandCompositor  *compositor, | ||||
|                                                                   GError                **error); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
							
								
								
									
										401
									
								
								src/wayland/meta-weston-launch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								src/wayland/meta-weston-launch.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,401 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #include <config.h> | ||||
|  | ||||
| #include <gio/gio.h> | ||||
| #include <gio/gunixfdmessage.h> | ||||
|  | ||||
| #include <clutter/clutter.h> | ||||
| #include <clutter/evdev/clutter-evdev.h> | ||||
|  | ||||
| #include <glib.h> | ||||
| #include <sys/time.h> | ||||
| #include <string.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
| #include <errno.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/un.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/wait.h> | ||||
|  | ||||
| #include <drm.h> | ||||
| #include <xf86drm.h> | ||||
| #include <xf86drmMode.h> | ||||
|  | ||||
| #include "meta-weston-launch.h" | ||||
|  | ||||
| struct _MetaLauncher | ||||
| { | ||||
|   GSocket *weston_launch; | ||||
|  | ||||
|   gboolean vt_switched; | ||||
|  | ||||
|   GMainContext *nested_context; | ||||
|   GMainLoop *nested_loop; | ||||
|  | ||||
|   GSource *inner_source; | ||||
|   GSource *outer_source; | ||||
| }; | ||||
|  | ||||
| static void handle_request_vt_switch (MetaLauncher *self); | ||||
|  | ||||
| static gboolean | ||||
| request_vt_switch_idle (gpointer user_data) | ||||
| { | ||||
|   handle_request_vt_switch (user_data); | ||||
|  | ||||
|   return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| send_message_to_wl (MetaLauncher           *self, | ||||
| 		    void                   *message, | ||||
| 		    gsize                   size, | ||||
| 		    GSocketControlMessage  *out_cmsg, | ||||
| 		    GSocketControlMessage **in_cmsg, | ||||
| 		    GError                **error) | ||||
| { | ||||
|   struct weston_launcher_reply reply; | ||||
|   GInputVector in_iov = { &reply, sizeof (reply) }; | ||||
|   GOutputVector out_iov = { message, size }; | ||||
|   GSocketControlMessage *out_all_cmsg[2]; | ||||
|   GSocketControlMessage **in_all_cmsg; | ||||
|   int flags = 0; | ||||
|   int i; | ||||
|  | ||||
|   out_all_cmsg[0] = out_cmsg; | ||||
|   out_all_cmsg[1] = NULL; | ||||
|   if (g_socket_send_message (self->weston_launch, NULL, | ||||
| 			     &out_iov, 1, | ||||
| 			     out_all_cmsg, -1, | ||||
| 			     flags, NULL, error) != (gssize)size) | ||||
|     return FALSE; | ||||
|  | ||||
|   if (g_socket_receive_message (self->weston_launch, NULL, | ||||
| 				&in_iov, 1, | ||||
| 			        &in_all_cmsg, NULL, | ||||
| 				&flags, NULL, error) != sizeof (reply)) | ||||
|     return FALSE; | ||||
|  | ||||
|   while (reply.header.opcode != ((struct weston_launcher_message*)message)->opcode) | ||||
|     { | ||||
|       /* There were events queued */ | ||||
|       g_assert ((reply.header.opcode & WESTON_LAUNCHER_EVENT) == WESTON_LAUNCHER_EVENT); | ||||
|  | ||||
|       /* This can never happen, because the only time mutter-launch can queue | ||||
|          this event is after confirming a VT switch, and we don't make requests | ||||
|          during that time. | ||||
|  | ||||
|          Note that getting this event would be really bad, because we would be | ||||
|          in the wrong loop/context. | ||||
|       */ | ||||
|       g_assert (reply.header.opcode != WESTON_LAUNCHER_SERVER_VT_ENTER); | ||||
|  | ||||
|       switch (reply.header.opcode) | ||||
| 	{ | ||||
| 	case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: | ||||
| 	  g_idle_add (request_vt_switch_idle, self); | ||||
| 	  break; | ||||
|  | ||||
| 	default: | ||||
| 	  g_assert_not_reached (); | ||||
| 	} | ||||
|  | ||||
|       if (g_socket_receive_message (self->weston_launch, NULL, | ||||
| 				    &in_iov, 1, | ||||
| 				    NULL, NULL, | ||||
| 				    &flags, NULL, error) != sizeof (reply)) | ||||
| 	return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (reply.ret != 0) | ||||
|     { | ||||
|       if (reply.ret == -1) | ||||
| 	g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, | ||||
| 		     "Got failure from weston-launch"); | ||||
|       else | ||||
| 	g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-reply.ret), | ||||
| 		     "Got failure from weston-launch: %s", strerror (-reply.ret)); | ||||
|  | ||||
|       for (i = 0; in_all_cmsg && in_all_cmsg[i]; i++) | ||||
| 	g_object_unref (in_all_cmsg[i]); | ||||
|       g_free (in_all_cmsg); | ||||
|  | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   if (in_all_cmsg && in_all_cmsg[0]) | ||||
|     { | ||||
|       for (i = 1; in_all_cmsg[i]; i++) | ||||
| 	g_object_unref (in_all_cmsg[i]); | ||||
|       *in_cmsg = in_all_cmsg[0]; | ||||
|     } | ||||
|  | ||||
|   g_free (in_all_cmsg); | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_launcher_set_drm_fd (MetaLauncher  *self, | ||||
| 			  int            drm_fd, | ||||
| 			  GError       **error) | ||||
| { | ||||
|   struct weston_launcher_message message; | ||||
|   GSocketControlMessage *cmsg; | ||||
|   gboolean ok; | ||||
|  | ||||
|   message.opcode = WESTON_LAUNCHER_DRM_SET_FD; | ||||
|  | ||||
|   cmsg = g_unix_fd_message_new (); | ||||
|   if (g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (cmsg), | ||||
| 				   drm_fd, error) == FALSE) | ||||
|     { | ||||
|       g_object_unref (cmsg); | ||||
|       return FALSE; | ||||
|     } | ||||
|  | ||||
|   ok = send_message_to_wl (self, &message, sizeof message, cmsg, NULL, error); | ||||
|  | ||||
|   g_object_unref (cmsg); | ||||
|   return ok; | ||||
| } | ||||
|  | ||||
| int | ||||
| meta_launcher_open_input_device (MetaLauncher  *self, | ||||
| 				 const char    *name, | ||||
| 				 int            flags, | ||||
| 				 GError       **error) | ||||
| { | ||||
|   struct weston_launcher_open *message; | ||||
|   GSocketControlMessage *cmsg; | ||||
|   gboolean ok; | ||||
|   gsize size; | ||||
|   int *fds, n_fd; | ||||
|   int ret; | ||||
|  | ||||
|   size = sizeof (struct weston_launcher_open) + strlen (name) + 1; | ||||
|   message = g_malloc (size); | ||||
|   message->header.opcode = WESTON_LAUNCHER_OPEN; | ||||
|   message->flags = flags; | ||||
|   strcpy (message->path, name); | ||||
|   message->path[strlen(name)] = 0; | ||||
|  | ||||
|   ok = send_message_to_wl (self, message, size, NULL, &cmsg, error); | ||||
|  | ||||
|   if (ok) | ||||
|     { | ||||
|       g_assert (G_IS_UNIX_FD_MESSAGE (cmsg)); | ||||
|  | ||||
|       fds = g_unix_fd_message_steal_fds (G_UNIX_FD_MESSAGE (cmsg), &n_fd); | ||||
|       g_assert (n_fd == 1); | ||||
|  | ||||
|       ret = fds[0]; | ||||
|       g_free (fds); | ||||
|       g_object_unref (cmsg); | ||||
|     } | ||||
|   else | ||||
|     ret = -1; | ||||
|  | ||||
|   g_free (message); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_enter (MetaLauncher *launcher) | ||||
| { | ||||
|   ClutterBackend *backend; | ||||
|   CoglContext *cogl_context; | ||||
|   CoglDisplay *cogl_display; | ||||
|  | ||||
|   backend = clutter_get_default_backend (); | ||||
|   cogl_context = clutter_backend_get_cogl_context (backend); | ||||
|   cogl_display = cogl_context_get_display (cogl_context); | ||||
|   cogl_kms_display_queue_modes_reset (cogl_display); | ||||
|  | ||||
|   clutter_evdev_reclaim_devices (); | ||||
| } | ||||
|  | ||||
| static void | ||||
| meta_launcher_leave (MetaLauncher *launcher) | ||||
| { | ||||
|   clutter_evdev_release_devices (); | ||||
| } | ||||
|  | ||||
| static int | ||||
| on_evdev_device_open (const char  *path, | ||||
| 		      int          flags, | ||||
| 		      gpointer     user_data, | ||||
| 		      GError     **error) | ||||
| { | ||||
|   MetaLauncher *launcher = user_data; | ||||
|  | ||||
|   return meta_launcher_open_input_device (launcher, path, flags, error); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_vt_enter (MetaLauncher *launcher) | ||||
| { | ||||
|   g_assert (launcher->vt_switched); | ||||
|  | ||||
|   g_main_loop_quit (launcher->nested_loop); | ||||
| } | ||||
|  | ||||
| static void | ||||
| handle_request_vt_switch (MetaLauncher *launcher) | ||||
| { | ||||
|   struct weston_launcher_message message; | ||||
|   GError *error; | ||||
|   gboolean ok; | ||||
|  | ||||
|   meta_launcher_leave (launcher); | ||||
|  | ||||
|   message.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; | ||||
|  | ||||
|   error = NULL; | ||||
|   ok = send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, &error); | ||||
|   if (!ok) { | ||||
|     g_warning ("Failed to acknowledge VT switch: %s", error->message); | ||||
|     g_error_free (error); | ||||
|  | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   g_assert (!launcher->vt_switched); | ||||
|   launcher->vt_switched = TRUE; | ||||
|  | ||||
|   /* We can't do anything at this point, because we don't | ||||
|      have input devices and we don't have the DRM master, | ||||
|      so let's run a nested busy loop until the VT is reentered */ | ||||
|   g_main_loop_run (launcher->nested_loop); | ||||
|  | ||||
|   g_assert (launcher->vt_switched); | ||||
|   launcher->vt_switched = FALSE; | ||||
|  | ||||
|   meta_launcher_enter (launcher); | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| on_socket_readable (GSocket      *socket, | ||||
| 		    GIOCondition  condition, | ||||
| 		    gpointer      user_data) | ||||
| { | ||||
|   MetaLauncher *launcher = user_data; | ||||
|   struct weston_launcher_event event; | ||||
|   gssize read; | ||||
|   GError *error; | ||||
|  | ||||
|   if ((condition & G_IO_IN) == 0) | ||||
|     return TRUE; | ||||
|  | ||||
|   error = NULL; | ||||
|   read = g_socket_receive (socket, (char*)&event, sizeof(event), NULL, &error); | ||||
|   if (read < (gssize)sizeof(event)) | ||||
|     { | ||||
|       g_warning ("Error reading from weston-launcher socket: %s", error->message); | ||||
|       g_error_free (error); | ||||
|       return TRUE; | ||||
|     } | ||||
|  | ||||
|   switch (event.header.opcode) | ||||
|     { | ||||
|     case WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH: | ||||
|       handle_request_vt_switch (launcher); | ||||
|       break; | ||||
|  | ||||
|     case WESTON_LAUNCHER_SERVER_VT_ENTER: | ||||
|       handle_vt_enter (launcher); | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| static int | ||||
| env_get_fd (const char *env) | ||||
| { | ||||
|   const char *value; | ||||
|  | ||||
|   value = g_getenv (env); | ||||
|  | ||||
|   if (value == NULL) | ||||
|     return -1; | ||||
|   else | ||||
|     return g_ascii_strtoll (value, NULL, 10); | ||||
| } | ||||
|  | ||||
| MetaLauncher * | ||||
| meta_launcher_new (void) | ||||
| { | ||||
|   MetaLauncher *self = g_slice_new0 (MetaLauncher); | ||||
|   int launch_fd; | ||||
|  | ||||
|   launch_fd = env_get_fd ("WESTON_LAUNCHER_SOCK"); | ||||
|   if (launch_fd < 0) | ||||
|     g_error ("Invalid mutter-launch socket"); | ||||
|  | ||||
|   self->weston_launch = g_socket_new_from_fd (launch_fd, NULL); | ||||
|  | ||||
|   clutter_evdev_set_open_callback (on_evdev_device_open, self); | ||||
|  | ||||
|   self->nested_context = g_main_context_new (); | ||||
|   self->nested_loop = g_main_loop_new (self->nested_context, FALSE); | ||||
|  | ||||
|   self->outer_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); | ||||
|   g_source_set_callback (self->outer_source, (GSourceFunc)on_socket_readable, self, NULL); | ||||
|   g_source_attach (self->outer_source, NULL); | ||||
|   g_source_unref (self->outer_source); | ||||
|  | ||||
|   self->inner_source = g_socket_create_source (self->weston_launch, G_IO_IN, NULL); | ||||
|   g_source_set_callback (self->inner_source, (GSourceFunc)on_socket_readable, self, NULL); | ||||
|   g_source_attach (self->inner_source, self->nested_context); | ||||
|   g_source_unref (self->inner_source); | ||||
|  | ||||
|   return self; | ||||
| } | ||||
|  | ||||
| void | ||||
| meta_launcher_free (MetaLauncher *launcher) | ||||
| { | ||||
|   g_source_destroy (launcher->outer_source); | ||||
|   g_source_destroy (launcher->inner_source); | ||||
|  | ||||
|   g_main_loop_unref (launcher->nested_loop); | ||||
|   g_main_context_unref (launcher->nested_context); | ||||
|  | ||||
|   g_object_unref (launcher->weston_launch); | ||||
|  | ||||
|   g_slice_free (MetaLauncher, launcher); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| meta_launcher_activate_vt (MetaLauncher  *launcher, | ||||
| 			   int            vt, | ||||
| 			   GError       **error) | ||||
| { | ||||
|   struct weston_launcher_activate_vt message; | ||||
|  | ||||
|   message.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; | ||||
|   message.vt = vt; | ||||
|  | ||||
|   return send_message_to_wl (launcher, &message, sizeof (message), NULL, NULL, error); | ||||
| } | ||||
|  | ||||
							
								
								
									
										46
									
								
								src/wayland/meta-weston-launch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/wayland/meta-weston-launch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * Copyright (C) 2013 Red Hat, Inc. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 of the | ||||
|  * License, or (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  */ | ||||
|  | ||||
| #ifndef META_WESTON_LAUNCH_H | ||||
| #define META_WESTON_LAUNCH_H | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include "weston-launch.h" | ||||
|  | ||||
| typedef struct _MetaLauncher MetaLauncher; | ||||
|  | ||||
| MetaLauncher     *meta_launcher_new                     (void); | ||||
| void              meta_launcher_free                    (MetaLauncher  *self); | ||||
|  | ||||
| gboolean          meta_launcher_activate_vt             (MetaLauncher  *self, | ||||
| 							 int            number, | ||||
| 							 GError       **error); | ||||
|  | ||||
| gboolean          meta_launcher_set_drm_fd              (MetaLauncher  *self, | ||||
| 							 int            drm_fd, | ||||
| 							 GError       **error); | ||||
| gboolean          meta_launcher_set_master              (MetaLauncher  *self, | ||||
| 							 gboolean       master, | ||||
| 							 GError       **error); | ||||
| int               meta_launcher_open_input_device       (MetaLauncher  *self, | ||||
| 							 const char    *name, | ||||
| 							 int            flags, | ||||
| 							 GError       **error); | ||||
|  | ||||
| #endif | ||||
| @@ -47,13 +47,6 @@ xserver_set_window_id (struct wl_client *client, | ||||
|   if (!window) | ||||
|     return; | ||||
|  | ||||
|   /* If the window has an existing surface, like if we're | ||||
|    * undecorating or decorating the window, then we need | ||||
|    * to detach the window from its old surface. | ||||
|    */ | ||||
|   if (window->surface) | ||||
|     window->surface->window = NULL; | ||||
|  | ||||
|   meta_wayland_surface_make_toplevel (surface); | ||||
|  | ||||
|   surface->window = window; | ||||
|   | ||||
							
								
								
									
										788
									
								
								src/wayland/weston-launch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										788
									
								
								src/wayland/weston-launch.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,788 @@ | ||||
| /* | ||||
|  * Copyright © 2012 Benjamin Franzke | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and | ||||
|  * its documentation for any purpose is hereby granted without fee, provided | ||||
|  * that the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of the copyright holders not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software | ||||
|  * without specific, written prior permission.  The copyright holders make | ||||
|  * no representations about the suitability of this software for any | ||||
|  * purpose.  It is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||||
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
|  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | ||||
|  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | ||||
|  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <assert.h> | ||||
| #include <poll.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include <error.h> | ||||
| #include <getopt.h> | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/wait.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/signalfd.h> | ||||
| #include <signal.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #include <termios.h> | ||||
| #include <linux/vt.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/kd.h> | ||||
|  | ||||
| #include <pwd.h> | ||||
| #include <grp.h> | ||||
|  | ||||
| #include <xf86drm.h> | ||||
|  | ||||
| #include <systemd/sd-login.h> | ||||
|  | ||||
| #include "weston-launch.h" | ||||
|  | ||||
| #define MAX_ARGV_SIZE 256 | ||||
| #define DRM_MAJOR     226 | ||||
|  | ||||
| enum vt_state { | ||||
| 	VT_HAS_VT, | ||||
| 	VT_PENDING_CONFIRM, | ||||
| 	VT_NOT_HAVE_VT, | ||||
| }; | ||||
|  | ||||
| struct weston_launch { | ||||
| 	int tty; | ||||
| 	int ttynr; | ||||
| 	int sock[2]; | ||||
| 	struct passwd *pw; | ||||
|  | ||||
| 	int signalfd; | ||||
|  | ||||
| 	pid_t child; | ||||
| 	int verbose; | ||||
|  | ||||
| 	struct termios terminal_attributes; | ||||
| 	int kb_mode; | ||||
| 	enum vt_state vt_state; | ||||
|  | ||||
|         int drm_fd; | ||||
| }; | ||||
|  | ||||
| union cmsg_data { unsigned char b[4]; int fd; }; | ||||
|  | ||||
| static void quit (struct weston_launch *wl, int status); | ||||
|  | ||||
| static int | ||||
| weston_launch_allowed(struct weston_launch *wl) | ||||
| { | ||||
| 	char *session, *seat; | ||||
| 	int err; | ||||
|  | ||||
| 	if (getuid() == 0) | ||||
| 		return 1; | ||||
|  | ||||
| 	err = sd_pid_get_session(getpid(), &session); | ||||
| 	if (err == 0 && session) { | ||||
| 		if (sd_session_is_active(session) && | ||||
| 		    sd_session_get_seat(session, &seat) == 0) { | ||||
| 			free(seat); | ||||
| 			free(session); | ||||
| 			return 1; | ||||
| 		} | ||||
| 		free(session); | ||||
| 	} | ||||
| 	 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_launcher_socket(struct weston_launch *wl) | ||||
| { | ||||
| 	if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0) | ||||
| 		error(1, errno, "socketpair failed"); | ||||
| 	 | ||||
| 	fcntl(wl->sock[0], F_SETFD, O_CLOEXEC); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_signals(struct weston_launch *wl) | ||||
| { | ||||
| 	int ret; | ||||
| 	sigset_t mask; | ||||
| 	struct sigaction sa; | ||||
|  | ||||
| 	memset(&sa, 0, sizeof sa); | ||||
| 	sa.sa_handler = SIG_DFL; | ||||
| 	sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; | ||||
| 	ret = sigaction(SIGCHLD, &sa, NULL); | ||||
| 	assert(ret == 0); | ||||
|  | ||||
| 	sa.sa_handler = SIG_IGN; | ||||
| 	sa.sa_flags = 0; | ||||
| 	sigaction(SIGHUP, &sa, NULL); | ||||
|  | ||||
| 	ret = sigemptyset(&mask); | ||||
| 	assert(ret == 0); | ||||
| 	sigaddset(&mask, SIGCHLD); | ||||
| 	sigaddset(&mask, SIGINT); | ||||
| 	sigaddset(&mask, SIGTERM); | ||||
| 	sigaddset(&mask, SIGUSR1); | ||||
| 	ret = sigprocmask(SIG_BLOCK, &mask, NULL); | ||||
| 	assert(ret == 0); | ||||
|  | ||||
| 	wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); | ||||
| 	if (wl->signalfd < 0) | ||||
| 		return -errno; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| setenv_fd(const char *env, int fd) | ||||
| { | ||||
| 	char buf[32]; | ||||
|  | ||||
| 	snprintf(buf, sizeof buf, "%d", fd); | ||||
| 	setenv(env, buf, 1); | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_setdrmfd(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
|         struct weston_launcher_reply reply; | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	union cmsg_data *data; | ||||
| 	struct stat s; | ||||
|  | ||||
| 	reply.header.opcode = WESTON_LAUNCHER_DRM_SET_FD; | ||||
| 	reply.ret = -1; | ||||
|  | ||||
| 	if (wl->drm_fd != -1) { | ||||
| 		error(0, 0, "DRM FD already set"); | ||||
| 		reply.ret = -EINVAL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	cmsg = CMSG_FIRSTHDR(msg); | ||||
| 	if (!cmsg || | ||||
| 	    cmsg->cmsg_level != SOL_SOCKET || | ||||
| 	    cmsg->cmsg_type != SCM_RIGHTS) { | ||||
| 		error(0, 0, "invalid control message"); | ||||
| 		reply.ret = -EINVAL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	data = (union cmsg_data *) CMSG_DATA(cmsg); | ||||
| 	if (data->fd < 0) { | ||||
| 		error(0, 0, "missing drm fd in socket request"); | ||||
| 		reply.ret = -EINVAL; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (fstat(data->fd, &s) < 0) { | ||||
| 		reply.ret = -errno; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (major(s.st_rdev) != DRM_MAJOR) { | ||||
| 		fprintf(stderr, "FD is not for DRM\n"); | ||||
| 		reply.ret = -EPERM; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	wl->drm_fd = data->fd; | ||||
| 	reply.ret = drmSetMaster(data->fd); | ||||
| 	if (reply.ret < 0) | ||||
| 		reply.ret = -errno; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "mutter-launch: set drm FD, ret: %d, fd: %d\n", | ||||
| 			reply.ret, data->fd); | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &reply, sizeof reply, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
|         struct weston_launcher_reply reply; | ||||
|  | ||||
| 	reply.header.opcode = WESTON_LAUNCHER_CONFIRM_VT_SWITCH; | ||||
| 	reply.ret = -1; | ||||
|  | ||||
| 	if (wl->vt_state != VT_PENDING_CONFIRM) { | ||||
| 		error(0, 0, "unexpected CONFIRM_VT_SWITCH"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (wl->drm_fd != -1) { | ||||
| 		int ret; | ||||
|  | ||||
| 		ret = drmDropMaster(wl->drm_fd); | ||||
| 		if (ret < 0) { | ||||
| 			fprintf(stderr, "failed to drop DRM master: %m\n"); | ||||
| 		} else if (wl->verbose) { | ||||
| 			fprintf(stderr, "dropped DRM master for VT switch\n"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	wl->vt_state = VT_NOT_HAVE_VT; | ||||
| 	ioctl(wl->tty, VT_RELDISP, 1); | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "mutter-launcher: confirmed VT switch\n"); | ||||
|  | ||||
| 	reply.ret = 0; | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &reply, sizeof reply, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
|         struct weston_launcher_reply reply; | ||||
| 	struct weston_launcher_activate_vt *message; | ||||
|  | ||||
| 	reply.header.opcode = WESTON_LAUNCHER_ACTIVATE_VT; | ||||
| 	reply.ret = -1; | ||||
|  | ||||
| 	if (len != sizeof(*message)) { | ||||
| 		error(0, 0, "missing value in activate_vt request"); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	message = msg->msg_iov->iov_base; | ||||
|  | ||||
| 	reply.ret = ioctl(wl->tty, VT_ACTIVATE, message->vt); | ||||
| 	if (reply.ret < 0) | ||||
| 		reply.ret = -errno; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "mutter-launch: activate VT, ret: %d\n", reply.ret); | ||||
|  | ||||
| out: | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &reply, sizeof reply, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len) | ||||
| { | ||||
|         struct weston_launcher_reply reply; | ||||
| 	int fd = -1; | ||||
| 	char control[CMSG_SPACE(sizeof(fd))]; | ||||
| 	struct cmsghdr *cmsg; | ||||
| 	struct stat s; | ||||
| 	struct msghdr nmsg; | ||||
| 	struct iovec iov; | ||||
| 	struct weston_launcher_open *message; | ||||
| 	union cmsg_data *data; | ||||
|  | ||||
| 	reply.header.opcode = WESTON_LAUNCHER_OPEN; | ||||
| 	reply.ret = -1; | ||||
|  | ||||
| 	message = msg->msg_iov->iov_base; | ||||
| 	if ((size_t)len < sizeof(*message)) | ||||
| 		goto err0; | ||||
|  | ||||
| 	/* Ensure path is null-terminated */ | ||||
| 	((char *) message)[len-1] = '\0'; | ||||
|  | ||||
| 	if (stat(message->path, &s) < 0) { | ||||
| 		reply.ret = -errno; | ||||
| 		goto err0; | ||||
| 	} | ||||
|  | ||||
| 	fd = open(message->path, message->flags); | ||||
| 	if (fd < 0) { | ||||
| 		fprintf(stderr, "Error opening device %s: %m\n", | ||||
| 			message->path); | ||||
| 		reply.ret = -errno; | ||||
| 		goto err0; | ||||
| 	} | ||||
|  | ||||
| 	if (major(s.st_rdev) != INPUT_MAJOR) { | ||||
| 		close(fd); | ||||
| 		fd = -1; | ||||
| 		fprintf(stderr, "Device %s is not an input device\n", | ||||
| 			message->path); | ||||
| 		reply.ret = -EPERM; | ||||
| 		goto err0; | ||||
| 	} | ||||
|  | ||||
| err0: | ||||
| 	memset(&nmsg, 0, sizeof nmsg); | ||||
| 	nmsg.msg_iov = &iov; | ||||
| 	nmsg.msg_iovlen = 1; | ||||
| 	if (fd != -1) { | ||||
| 		nmsg.msg_control = control; | ||||
| 		nmsg.msg_controllen = sizeof control; | ||||
| 		cmsg = CMSG_FIRSTHDR(&nmsg); | ||||
| 		cmsg->cmsg_level = SOL_SOCKET; | ||||
| 		cmsg->cmsg_type = SCM_RIGHTS; | ||||
| 		cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); | ||||
| 		data = (union cmsg_data *) CMSG_DATA(cmsg); | ||||
| 		data->fd = fd; | ||||
| 		nmsg.msg_controllen = cmsg->cmsg_len; | ||||
| 		reply.ret = 0; | ||||
| 	} | ||||
| 	iov.iov_base = &reply; | ||||
| 	iov.iov_len = sizeof reply; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		fprintf(stderr, "mutter-launch: opened %s: ret: %d, fd: %d\n", | ||||
| 			message->path, reply.ret, fd); | ||||
| 	do { | ||||
| 		len = sendmsg(wl->sock[0], &nmsg, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	close(fd); | ||||
|  | ||||
| 	if (len < 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_socket_msg(struct weston_launch *wl) | ||||
| { | ||||
| 	char control[CMSG_SPACE(sizeof(int))]; | ||||
| 	char buf[BUFSIZ]; | ||||
| 	struct msghdr msg; | ||||
| 	struct iovec iov; | ||||
| 	int ret = -1; | ||||
| 	ssize_t len; | ||||
| 	struct weston_launcher_message *message; | ||||
|  | ||||
| 	memset(&msg, 0, sizeof(msg)); | ||||
| 	iov.iov_base = buf; | ||||
| 	iov.iov_len  = sizeof buf; | ||||
| 	msg.msg_iov = &iov; | ||||
| 	msg.msg_iovlen = 1; | ||||
| 	msg.msg_control = control; | ||||
| 	msg.msg_controllen = sizeof control; | ||||
|  | ||||
| 	do { | ||||
| 		len = recvmsg(wl->sock[0], &msg, 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	if (len < 1) | ||||
| 		return -1; | ||||
|  | ||||
| 	message = (void *) buf; | ||||
| 	switch (message->opcode) { | ||||
| 	case WESTON_LAUNCHER_OPEN: | ||||
| 		ret = handle_open(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_DRM_SET_FD: | ||||
| 		ret = handle_setdrmfd(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_CONFIRM_VT_SWITCH: | ||||
| 		ret = handle_confirm_vt_switch(wl, &msg, len); | ||||
| 		break; | ||||
| 	case WESTON_LAUNCHER_ACTIVATE_VT: | ||||
| 		ret = handle_activate_vt(wl, &msg, len); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void | ||||
| tty_reset(struct weston_launch *wl) | ||||
| { | ||||
| 	struct vt_mode mode = { 0 }; | ||||
|  | ||||
| 	if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) | ||||
| 		fprintf(stderr, "failed to restore keyboard mode: %m\n"); | ||||
|  | ||||
| 	if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) | ||||
| 		fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n"); | ||||
|  | ||||
| 	if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0) | ||||
| 		fprintf(stderr, "could not restore terminal to canonical mode\n"); | ||||
|  | ||||
| 	mode.mode = VT_AUTO; | ||||
| 	if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) | ||||
| 		fprintf(stderr, "could not reset vt handling\n"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| quit(struct weston_launch *wl, int status) | ||||
| { | ||||
| 	if (wl->child > 0) | ||||
| 		kill(wl->child, SIGKILL); | ||||
|  | ||||
| 	close(wl->signalfd); | ||||
| 	close(wl->sock[0]); | ||||
|  | ||||
| 	if (wl->drm_fd > 0) | ||||
| 		close(wl->drm_fd); | ||||
|  | ||||
| 	tty_reset(wl); | ||||
|  | ||||
| 	exit(status); | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_vt_switch(struct weston_launch *wl) | ||||
| { | ||||
| 	struct weston_launcher_event message; | ||||
| 	ssize_t len; | ||||
|  | ||||
| 	if (wl->vt_state == VT_HAS_VT) { | ||||
| 		wl->vt_state = VT_PENDING_CONFIRM; | ||||
| 		message.header.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH; | ||||
| 	} else if (wl->vt_state == VT_NOT_HAVE_VT) { | ||||
| 		wl->vt_state = VT_HAS_VT; | ||||
| 		ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); | ||||
|  | ||||
| 		if (wl->drm_fd != -1) { | ||||
| 			int ret; | ||||
|  | ||||
| 			ret = drmSetMaster(wl->drm_fd); | ||||
| 			if (ret < 0) { | ||||
| 				fprintf(stderr, "failed to become DRM master: %m\n"); | ||||
| 				/* This is very, very bad, and the compositor will crash soon, | ||||
| 				   but oh well... */ | ||||
| 			} else if (wl->verbose) { | ||||
| 				fprintf(stderr, "became DRM master after VT switch\n"); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		message.header.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER; | ||||
| 	} else | ||||
| 		return -1; | ||||
|  | ||||
| 	message.detail = 0; | ||||
|  | ||||
| 	do { | ||||
| 		len = send(wl->sock[0], &message, sizeof(message), 0); | ||||
| 	} while (len < 0 && errno == EINTR); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int | ||||
| handle_signal(struct weston_launch *wl) | ||||
| { | ||||
| 	struct signalfd_siginfo sig; | ||||
| 	int pid, status, ret; | ||||
|  | ||||
| 	if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { | ||||
| 		error(0, errno, "reading signalfd failed"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	switch (sig.ssi_signo) { | ||||
| 	case SIGCHLD: | ||||
| 		pid = waitpid(-1, &status, 0); | ||||
| 		if (pid == wl->child) { | ||||
| 			wl->child = 0; | ||||
| 			if (WIFEXITED(status)) | ||||
| 				ret = WEXITSTATUS(status); | ||||
| 			else if (WIFSIGNALED(status)) | ||||
| 				/* | ||||
| 				 * If weston dies because of signal N, we | ||||
| 				 * return 10+N. This is distinct from | ||||
| 				 * weston-launch dying because of a signal | ||||
| 				 * (128+N). | ||||
| 				 */ | ||||
| 				ret = 10 + WTERMSIG(status); | ||||
| 			else | ||||
| 				ret = 0; | ||||
| 			quit(wl, ret); | ||||
| 		} | ||||
| 		break; | ||||
| 	case SIGTERM: | ||||
| 	case SIGINT: | ||||
| 		if (wl->child) | ||||
| 			kill(wl->child, sig.ssi_signo); | ||||
| 		break; | ||||
| 	case SIGUSR1: | ||||
| 		return handle_vt_switch(wl); | ||||
| 	default: | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_tty(struct weston_launch *wl) | ||||
| { | ||||
| 	struct stat buf; | ||||
| 	struct termios raw_attributes; | ||||
| 	struct vt_mode mode = { 0 }; | ||||
| 	char *session, *tty; | ||||
| 	char path[PATH_MAX]; | ||||
| 	int ok; | ||||
|  | ||||
| 	ok = sd_pid_get_session(getpid(), &session); | ||||
| 	if (ok < 0) | ||||
| 	  error(1, -ok, "could not determine current session"); | ||||
|  | ||||
| 	ok = sd_session_get_tty(session, &tty); | ||||
| 	if (ok == 0) { | ||||
| 		/* Old systemd only has the tty name in the TTY | ||||
| 		   field, new one has the full char device path. | ||||
|  | ||||
| 		   Check what we have and fix it properly. | ||||
| 		*/ | ||||
| 		if (strncmp(tty, "/dev", strlen("/dev")) == 0) { | ||||
| 			strncpy(path, tty, PATH_MAX); | ||||
| 			path[PATH_MAX-1] = 0; | ||||
| 		} else { | ||||
| 			snprintf(path, PATH_MAX, "/dev/%s", tty); | ||||
| 		} | ||||
|  | ||||
| 		wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); | ||||
| 		free(tty); | ||||
| #ifdef HAVE_SD_SESSION_GET_VT | ||||
| 	} else if (ok == -ENOENT) { | ||||
| 		unsigned vt; | ||||
|  | ||||
| 		/* Negative errnos are cool, right? | ||||
| 		   So cool that we can't distinguish "session not found" | ||||
| 		   from "key does not exist in the session file"! | ||||
| 		   Let's assume the latter, as we got the value | ||||
| 		   from sd_pid_get_session()... | ||||
| 		*/ | ||||
|  | ||||
| 		ok = sd_session_get_vt(session, &vt); | ||||
| 		if (ok < 0) | ||||
| 			error(1, -ok, "could not determine current TTY"); | ||||
|  | ||||
| 		snprintf(path, PATH_MAX, "/dev/tty%u", vt); | ||||
| 		wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); | ||||
| 		free(tty); | ||||
| #endif | ||||
| 	} else | ||||
| 		error(1, -ok, "could not determine current TTY"); | ||||
|  | ||||
| 	if (wl->tty < 0) | ||||
| 		error(1, errno, "failed to open tty"); | ||||
|  | ||||
| 	if (fstat(wl->tty, &buf) < 0) | ||||
| 		error(1, errno, "stat %s failed", path); | ||||
|  | ||||
| 	if (major(buf.st_rdev) != TTY_MAJOR) | ||||
| 		error(1, 0, "invalid tty device: %s", path); | ||||
|  | ||||
| 	wl->ttynr = minor(buf.st_rdev); | ||||
|  | ||||
| 	if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0) | ||||
| 		error(1, errno, "could not get terminal attributes"); | ||||
|  | ||||
| 	/* Ignore control characters and disable echo */ | ||||
| 	raw_attributes = wl->terminal_attributes; | ||||
| 	cfmakeraw(&raw_attributes); | ||||
|  | ||||
| 	/* Fix up line endings to be normal (cfmakeraw hoses them) */ | ||||
| 	raw_attributes.c_oflag |= OPOST | OCRNL; | ||||
| 	/* Don't generate ttou signals */ | ||||
| 	raw_attributes.c_oflag &= ~TOSTOP; | ||||
|  | ||||
| 	if (tcsetattr(wl->tty, TCSANOW, &raw_attributes) < 0) | ||||
| 	        error(1, errno, "could not put terminal into raw mode"); | ||||
|  | ||||
| 	ioctl(wl->tty, KDGKBMODE, &wl->kb_mode); | ||||
| 	ok = ioctl(wl->tty, KDSKBMODE, K_OFF); | ||||
| 	if (ok < 0) { | ||||
| 		ok = ioctl(wl->tty, KDSKBMODE, K_RAW); | ||||
| 		if (ok < 0) | ||||
| 			error(1, errno, "failed to set keyboard mode on tty"); | ||||
| 	} | ||||
|  | ||||
| 	ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS); | ||||
| 	if (ok < 0) | ||||
| 		error(1, errno, "failed to set KD_GRAPHICS mode on tty"); | ||||
|  | ||||
| 	wl->vt_state = VT_HAS_VT; | ||||
| 	mode.mode = VT_PROCESS; | ||||
| 	mode.relsig = SIGUSR1; | ||||
| 	mode.acqsig = SIGUSR1; | ||||
| 	ok = ioctl(wl->tty, VT_SETMODE, &mode); | ||||
| 	if (ok < 0) | ||||
| 		error(1, errno, "failed to take control of vt handling"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| drop_privileges(struct weston_launch *wl) | ||||
| { | ||||
| 	if (setgid(wl->pw->pw_gid) < 0 || | ||||
| #ifdef HAVE_INITGROUPS | ||||
| 	    initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 || | ||||
| #endif | ||||
| 	    setuid(wl->pw->pw_uid) < 0) | ||||
| 		error(1, errno, "dropping privileges failed"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| launch_compositor(struct weston_launch *wl, int argc, char *argv[]) | ||||
| { | ||||
| 	char command[PATH_MAX]; | ||||
| 	char *child_argv[MAX_ARGV_SIZE]; | ||||
| 	sigset_t mask; | ||||
| 	int i; | ||||
|  | ||||
| 	if (wl->verbose) | ||||
| 		printf("weston-launch: spawned weston with pid: %d\n", getpid()); | ||||
|  | ||||
| 	drop_privileges(wl); | ||||
|  | ||||
| 	setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]); | ||||
| 	setenv("LD_LIBRARY_PATH", LIBDIR, 1); | ||||
| 	unsetenv("DISPLAY"); | ||||
|  | ||||
| 	/* Do not give our signal mask to the new process. */ | ||||
| 	sigemptyset(&mask); | ||||
| 	sigaddset(&mask, SIGTERM); | ||||
| 	sigaddset(&mask, SIGCHLD); | ||||
| 	sigaddset(&mask, SIGINT); | ||||
| 	sigaddset(&mask, SIGUSR1); | ||||
| 	sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||||
|  | ||||
| 	snprintf (command, PATH_MAX, "%s \"$@\"", argv[0]); | ||||
|  | ||||
| 	child_argv[0] = wl->pw->pw_shell; | ||||
| 	child_argv[1] = "-l"; | ||||
| 	child_argv[2] = "-c"; | ||||
| 	child_argv[3] = command; | ||||
| 	for (i = 0; i < argc; ++i) | ||||
| 		child_argv[4 + i] = argv[i]; | ||||
| 	child_argv[4 + i] = NULL; | ||||
|  | ||||
| 	execv(child_argv[0], child_argv); | ||||
| 	error(1, errno, "exec failed"); | ||||
| } | ||||
|  | ||||
| static void | ||||
| help(const char *name) | ||||
| { | ||||
| 	fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name); | ||||
| 	fprintf(stderr, "  -u, --user      Start session as specified username\n"); | ||||
| 	fprintf(stderr, "  -v, --verbose   Be verbose\n"); | ||||
| 	fprintf(stderr, "  -h, --help      Display this help message\n"); | ||||
| } | ||||
|  | ||||
| int | ||||
| main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct weston_launch wl; | ||||
| 	int i, c; | ||||
| 	struct option opts[] = { | ||||
| 		{ "verbose", no_argument,       NULL, 'v' }, | ||||
| 		{ "help",    no_argument,       NULL, 'h' }, | ||||
| 		{ 0,         0,                 NULL,  0  } | ||||
| 	};	 | ||||
|  | ||||
| 	memset(&wl, 0, sizeof wl); | ||||
| 	wl.drm_fd = -1; | ||||
|  | ||||
| 	while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) { | ||||
| 		switch (c) { | ||||
| 		case 'v': | ||||
| 			wl.verbose = 1; | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			help("mutter-launch"); | ||||
| 			exit(EXIT_FAILURE); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if ((argc - optind) > (MAX_ARGV_SIZE - 6)) | ||||
| 		error(1, E2BIG, "Too many arguments to pass to weston"); | ||||
|  | ||||
| 	if (optind >= argc) | ||||
| 		error(1, 0, "Expected program argument"); | ||||
|  | ||||
| 	wl.pw = getpwuid(getuid()); | ||||
| 	if (wl.pw == NULL) | ||||
| 		error(1, errno, "failed to get username"); | ||||
|  | ||||
| 	if (!weston_launch_allowed(&wl)) | ||||
| 		error(1, 0, "Permission denied. You must run from an active and local (systemd) session."); | ||||
|  | ||||
| 	if (setup_tty(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	if (setup_launcher_socket(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	if (setup_signals(&wl) < 0) | ||||
| 		exit(EXIT_FAILURE); | ||||
|  | ||||
| 	wl.child = fork(); | ||||
| 	if (wl.child == -1) { | ||||
| 		error(1, errno, "fork failed"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	if (wl.child == 0) | ||||
| 		launch_compositor(&wl, argc - optind, argv + optind); | ||||
|  | ||||
| 	close(wl.sock[1]); | ||||
|  | ||||
| 	while (1) { | ||||
| 		struct pollfd fds[2]; | ||||
| 		int n; | ||||
|  | ||||
| 		fds[0].fd = wl.sock[0]; | ||||
| 		fds[0].events = POLLIN; | ||||
| 		fds[1].fd = wl.signalfd; | ||||
| 		fds[1].events = POLLIN; | ||||
|  | ||||
| 		n = poll(fds, 2, -1); | ||||
| 		if (n < 0) | ||||
| 			error(0, errno, "poll failed"); | ||||
| 		if (fds[0].revents & POLLIN) | ||||
| 			handle_socket_msg(&wl); | ||||
| 		if (fds[1].revents) | ||||
| 			handle_signal(&wl); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/wayland/weston-launch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/wayland/weston-launch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * Copyright © 2012 Benjamin Franzke | ||||
|  *             2013 Red Hat, Inc. | ||||
|  * | ||||
|  * Permission to use, copy, modify, distribute, and sell this software and | ||||
|  * its documentation for any purpose is hereby granted without fee, provided | ||||
|  * that the above copyright notice appear in all copies and that both that | ||||
|  * copyright notice and this permission notice appear in supporting | ||||
|  * documentation, and that the name of the copyright holders not be used in | ||||
|  * advertising or publicity pertaining to distribution of the software | ||||
|  * without specific, written prior permission.  The copyright holders make | ||||
|  * no representations about the suitability of this software for any | ||||
|  * purpose.  It is provided "as is" without express or implied warranty. | ||||
|  * | ||||
|  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||||
|  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||
|  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER | ||||
|  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF | ||||
|  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #ifndef _WESTON_LAUNCH_H_ | ||||
| #define _WESTON_LAUNCH_H_ | ||||
|  | ||||
| enum weston_launcher_message_type { | ||||
| 	WESTON_LAUNCHER_REQUEST, | ||||
| 	WESTON_LAUNCHER_EVENT, | ||||
| }; | ||||
|  | ||||
| enum weston_launcher_opcode { | ||||
| 	WESTON_LAUNCHER_OPEN              = (1 << 1 | WESTON_LAUNCHER_REQUEST), | ||||
| 	WESTON_LAUNCHER_DRM_SET_FD        = (2 << 1 | WESTON_LAUNCHER_REQUEST), | ||||
| 	WESTON_LAUNCHER_ACTIVATE_VT       = (3 << 1 | WESTON_LAUNCHER_REQUEST), | ||||
| 	WESTON_LAUNCHER_CONFIRM_VT_SWITCH = (4 << 1 | WESTON_LAUNCHER_REQUEST), | ||||
| }; | ||||
|  | ||||
| enum weston_launcher_server_opcode { | ||||
| 	WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH = (1 << 1 | WESTON_LAUNCHER_EVENT), | ||||
| 	WESTON_LAUNCHER_SERVER_VT_ENTER          = (2 << 1 | WESTON_LAUNCHER_EVENT), | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_message { | ||||
| 	int opcode; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_open { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int flags; | ||||
| 	char path[0]; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_activate_vt { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int vt; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_reply { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int ret; | ||||
| }; | ||||
|  | ||||
| struct weston_launcher_event { | ||||
| 	struct weston_launcher_message header; | ||||
| 	int detail; /* unused, but makes sure replies and events are serialized the same */ | ||||
| }; | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user