1 /*
2  * window-tracker-monitor: A monitor object tracked by window tracker.
3  *                         It provides information about position and
4  *                         size of monitor within screen and also a flag
5  *                         if this monitor is the primary one.
6  *
7  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22  * MA 02110-1301, USA.
23  *
24  *
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <libxfdashboard/x11/window-tracker-monitor-x11.h>
32 
33 #include <glib/gi18n-lib.h>
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 
37 #include <libxfdashboard/window-tracker-monitor.h>
38 #include <libxfdashboard/compat.h>
39 #include <libxfdashboard/debug.h>
40 
41 
42 /* Define this class in GObject system */
43 static void _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_iface_init(XfdashboardWindowTrackerMonitorInterface *iface);
44 
45 struct _XfdashboardWindowTrackerMonitorX11Private
46 {
47 	/* Properties related */
48 	gint				monitorIndex;
49 	gboolean			isPrimary;
50 
51 	/* Instance related */
52 	GdkScreen			*screen;
53 	GdkRectangle		geometry;
54 };
55 
56 G_DEFINE_TYPE_WITH_CODE(XfdashboardWindowTrackerMonitorX11,
57 						xfdashboard_window_tracker_monitor_x11,
58 						G_TYPE_OBJECT,
59 						G_ADD_PRIVATE(XfdashboardWindowTrackerMonitorX11)
60 						G_IMPLEMENT_INTERFACE(XFDASHBOARD_TYPE_WINDOW_TRACKER_MONITOR, _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_iface_init))
61 
62 /* Properties */
63 enum
64 {
65 	PROP_0,
66 
67 	/* Overriden properties of interface: XfdashboardWindowTrackerMonitor */
68 	PROP_MONITOR_INDEX,
69 	PROP_IS_PRIMARY,
70 
71 	PROP_LAST
72 };
73 
74 static GParamSpec* XfdashboardWindowTrackerMonitorX11Properties[PROP_LAST]={ 0, };
75 
76 
77 /* IMPLEMENTATION: Private variables and methods */
78 
79 /* Set primary monitor flag */
_xfdashboard_window_tracker_monitor_x11_update_primary(XfdashboardWindowTrackerMonitorX11 * self)80 static void _xfdashboard_window_tracker_monitor_x11_update_primary(XfdashboardWindowTrackerMonitorX11 *self)
81 {
82 	XfdashboardWindowTrackerMonitorX11Private	*priv;
83 	gboolean									isPrimary;
84 #if GTK_CHECK_VERSION(3, 22, 0)
85 	GdkMonitor									*primaryMonitor;
86 #else
87 	gint										primaryMonitor;
88 #endif
89 
90 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR(self));
91 	g_return_if_fail(self->priv->monitorIndex>=0);
92 
93 	priv=self->priv;
94 
95 	/* Get primary flag */
96 #if GTK_CHECK_VERSION(3, 22, 0)
97 	primaryMonitor=gdk_display_get_monitor(gdk_screen_get_display(priv->screen), priv->monitorIndex);
98 	isPrimary=gdk_monitor_is_primary(primaryMonitor);
99 #else
100 	primaryMonitor=gdk_screen_get_primary_monitor(priv->screen);
101 	if(primaryMonitor==priv->monitorIndex) isPrimary=TRUE;
102 		else isPrimary=FALSE;
103 #endif
104 
105 	/* Set value if changed */
106 	if(priv->isPrimary!=isPrimary)
107 	{
108 		XFDASHBOARD_DEBUG(self, WINDOWS,
109 							"Monitor %d changes primary state from %s to %s",
110 							priv->monitorIndex,
111 							priv->isPrimary ? "yes" : "no",
112 							isPrimary ? "yes" : "no");
113 
114 		/* Set value */
115 		priv->isPrimary=isPrimary;
116 
117 		/* Notify about property change */
118 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardWindowTrackerMonitorX11Properties[PROP_IS_PRIMARY]);
119 
120 		/* Emit signal */
121 		g_signal_emit_by_name(self, "primary-changed");
122 	}
123 }
124 
125 /* Update monitor geometry */
_xfdashboard_window_tracker_monitor_x11_update_geometry(XfdashboardWindowTrackerMonitorX11 * self)126 static void _xfdashboard_window_tracker_monitor_x11_update_geometry(XfdashboardWindowTrackerMonitorX11 *self)
127 {
128 	XfdashboardWindowTrackerMonitorX11Private	*priv;
129 	GdkRectangle								geometry;
130 	gint										numberMonitors;
131 #if GTK_CHECK_VERSION(3, 22, 0)
132 	GdkDisplay									*display;
133 	GdkMonitor									*monitor;
134 #endif
135 
136 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(self));
137 	g_return_if_fail(self->priv->monitorIndex>=0);
138 
139 	priv=self->priv;
140 
141 	/* Get number of monitors */
142 #if GTK_CHECK_VERSION(3, 22, 0)
143 	display=gdk_screen_get_display(priv->screen);
144 	numberMonitors=gdk_display_get_n_monitors(display);
145 #else
146 	numberMonitors=gdk_screen_get_n_monitors(priv->screen);
147 #endif
148 
149 	/* Check if monitor is valid */
150 	if(priv->monitorIndex>=numberMonitors) return;
151 
152 	/* Get monitor geometry */
153 #if GTK_CHECK_VERSION(3, 22, 0)
154 	monitor=gdk_display_get_monitor(display, priv->monitorIndex);
155 	gdk_monitor_get_geometry(monitor, &geometry);
156 #else
157 	gdk_screen_get_monitor_geometry(priv->screen, priv->monitorIndex, &geometry);
158 #endif
159 
160 	/* Set value if changed */
161 	if(geometry.x!=priv->geometry.x ||
162 		geometry.y!=priv->geometry.y ||
163 		geometry.width!=priv->geometry.width ||
164 		geometry.height!=priv->geometry.height)
165 	{
166 		/* Set value */
167 		priv->geometry.x=geometry.x;
168 		priv->geometry.y=geometry.y;
169 		priv->geometry.width=geometry.width;
170 		priv->geometry.height=geometry.height;
171 
172 		/* Emit signal */
173 		g_signal_emit_by_name(self, "geometry-changed");
174 		XFDASHBOARD_DEBUG(self, WINDOWS,
175 							"Monitor %d moved to %d,%d and resized to %dx%d",
176 							priv->monitorIndex,
177 							priv->geometry.x, priv->geometry.y,
178 							priv->geometry.width, priv->geometry.height);
179 	}
180 }
181 
182 /* Number of monitors, primary monitor or size of any monitor changed */
_xfdashboard_window_tracker_monitor_x11_on_monitors_changed(XfdashboardWindowTrackerMonitorX11 * self,gpointer inUserData)183 static void _xfdashboard_window_tracker_monitor_x11_on_monitors_changed(XfdashboardWindowTrackerMonitorX11 *self,
184 																		gpointer inUserData)
185 {
186 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(self));
187 	g_return_if_fail(GDK_IS_SCREEN(inUserData));
188 
189 	/* Update primary monitor flag */
190 	_xfdashboard_window_tracker_monitor_x11_update_primary(self);
191 
192 	/* Update geometry of monitor */
193 	_xfdashboard_window_tracker_monitor_x11_update_geometry(self);
194 }
195 
196 /* Set monitor index this object belongs to and to monitor */
_xfdashboard_window_tracker_monitor_x11_set_index(XfdashboardWindowTrackerMonitorX11 * self,gint inIndex)197 static void _xfdashboard_window_tracker_monitor_x11_set_index(XfdashboardWindowTrackerMonitorX11 *self,
198 																gint inIndex)
199 {
200 	XfdashboardWindowTrackerMonitorX11Private		*priv;
201 	gint											numberMonitors;
202 
203 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(self));
204 	g_return_if_fail(inIndex>=0);
205 
206 	priv=self->priv;
207 
208 	/* Get number of monitors */
209 #if GTK_CHECK_VERSION(3, 22, 0)
210 	numberMonitors=gdk_display_get_n_monitors(gdk_screen_get_display(priv->screen));
211 #else
212 	numberMonitors=gdk_screen_get_n_monitors(priv->screen);
213 #endif
214 	g_return_if_fail(inIndex<numberMonitors);
215 
216 	/* Freeze notification */
217 	g_object_freeze_notify(G_OBJECT(self));
218 
219 	/* Set value if changed */
220 	if(priv->monitorIndex!=inIndex)
221 	{
222 		/* Set value */
223 		priv->monitorIndex=inIndex;
224 
225 		/* Update primary monitor flag */
226 		_xfdashboard_window_tracker_monitor_x11_update_primary(self);
227 
228 		/* Update geometry of monitor */
229 		_xfdashboard_window_tracker_monitor_x11_update_geometry(self);
230 
231 		/* Connect signals now we have a valid monitor index set */
232 		g_signal_connect_swapped(priv->screen, "monitors-changed", G_CALLBACK(_xfdashboard_window_tracker_monitor_x11_on_monitors_changed), self);
233 
234 		/* Notify about property change */
235 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardWindowTrackerMonitorX11Properties[PROP_MONITOR_INDEX]);
236 	}
237 
238 	/* Thaw notification */
239 	g_object_thaw_notify(G_OBJECT(self));
240 }
241 
242 
243 /* IMPLEMENTATION: Interface XfdashboardWindowTrackerMonitor */
244 
245 /* Determine if monitor is primary one */
_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_is_primary(XfdashboardWindowTrackerMonitor * inMonitor)246 static gboolean _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_is_primary(XfdashboardWindowTrackerMonitor *inMonitor)
247 {
248 	XfdashboardWindowTrackerMonitorX11			*self;
249 	XfdashboardWindowTrackerMonitorX11Private	*priv;
250 
251 	g_return_val_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(inMonitor), FALSE);
252 
253 	self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inMonitor);
254 	priv=self->priv;
255 
256 	return(priv->isPrimary);
257 }
258 
259 /* Get monitor index */
_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_number(XfdashboardWindowTrackerMonitor * inMonitor)260 static gint _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_number(XfdashboardWindowTrackerMonitor *inMonitor)
261 {
262 	XfdashboardWindowTrackerMonitorX11			*self;
263 	XfdashboardWindowTrackerMonitorX11Private	*priv;
264 
265 	g_return_val_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(inMonitor), 0);
266 
267 	self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inMonitor);
268 	priv=self->priv;
269 
270 	return(priv->monitorIndex);
271 }
272 
273 /* Get geometry of monitor */
_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_geometry(XfdashboardWindowTrackerMonitor * inMonitor,gint * outX,gint * outY,gint * outWidth,gint * outHeight)274 static void _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_geometry(XfdashboardWindowTrackerMonitor *inMonitor,
275 																							gint *outX,
276 																							gint *outY,
277 																							gint *outWidth,
278 																							gint *outHeight)
279 {
280 	XfdashboardWindowTrackerMonitorX11			*self;
281 	XfdashboardWindowTrackerMonitorX11Private	*priv;
282 
283 	g_return_if_fail(XFDASHBOARD_IS_WINDOW_TRACKER_MONITOR_X11(inMonitor));
284 
285 	self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inMonitor);
286 	priv=self->priv;
287 
288 	/* Set position and size of monitor */
289 	if(outX) *outX=priv->geometry.x;
290 	if(outY) *outY=priv->geometry.y;
291 	if(outWidth) *outWidth=priv->geometry.width;
292 	if(outHeight) *outHeight=priv->geometry.height;
293 }
294 
295 /* Interface initialization
296  * Set up default functions
297  */
_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_iface_init(XfdashboardWindowTrackerMonitorInterface * iface)298 static void _xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_iface_init(XfdashboardWindowTrackerMonitorInterface *iface)
299 {
300 	iface->is_primary=_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_is_primary;
301 	iface->get_number=_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_number;
302 	iface->get_geometry=_xfdashboard_window_tracker_monitor_x11_x11_window_tracker_monitor_get_geometry;
303 }
304 
305 
306 /* IMPLEMENTATION: GObject */
307 
308 /* Dispose this object */
_xfdashboard_window_tracker_monitor_x11_dispose(GObject * inObject)309 static void _xfdashboard_window_tracker_monitor_x11_dispose(GObject *inObject)
310 {
311 	XfdashboardWindowTrackerMonitorX11			*self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inObject);
312 	XfdashboardWindowTrackerMonitorX11Private	*priv=self->priv;
313 
314 	/* Release allocated resources */
315 	if(priv->screen)
316 	{
317 		g_signal_handlers_disconnect_by_data(priv->screen, self);
318 		priv->screen=NULL;
319 	}
320 
321 	/* Call parent's class dispose method */
322 	G_OBJECT_CLASS(xfdashboard_window_tracker_monitor_x11_parent_class)->dispose(inObject);
323 }
324 
325 /* Set/get properties */
_xfdashboard_window_tracker_monitor_x11_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)326 static void _xfdashboard_window_tracker_monitor_x11_set_property(GObject *inObject,
327 																	guint inPropID,
328 																	const GValue *inValue,
329 																	GParamSpec *inSpec)
330 {
331 	XfdashboardWindowTrackerMonitorX11			*self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inObject);
332 
333 	switch(inPropID)
334 	{
335 		case PROP_MONITOR_INDEX:
336 			_xfdashboard_window_tracker_monitor_x11_set_index(self, g_value_get_int(inValue));
337 			break;
338 
339 		default:
340 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
341 			break;
342 	}
343 }
344 
_xfdashboard_window_tracker_monitor_x11_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)345 static void _xfdashboard_window_tracker_monitor_x11_get_property(GObject *inObject,
346 																	guint inPropID,
347 																	GValue *outValue,
348 																	GParamSpec *inSpec)
349 {
350 	XfdashboardWindowTrackerMonitorX11			*self=XFDASHBOARD_WINDOW_TRACKER_MONITOR_X11(inObject);
351 	XfdashboardWindowTrackerMonitorX11Private	*priv=self->priv;
352 
353 	switch(inPropID)
354 	{
355 		case PROP_IS_PRIMARY:
356 			g_value_set_boolean(outValue, priv->isPrimary);
357 			break;
358 
359 		case PROP_MONITOR_INDEX:
360 			g_value_set_uint(outValue, priv->monitorIndex);
361 			break;
362 
363 		default:
364 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
365 			break;
366 	}
367 }
368 
369 /* Class initialization
370  * Override functions in parent classes and define properties
371  * and signals
372  */
xfdashboard_window_tracker_monitor_x11_class_init(XfdashboardWindowTrackerMonitorX11Class * klass)373 static void xfdashboard_window_tracker_monitor_x11_class_init(XfdashboardWindowTrackerMonitorX11Class *klass)
374 {
375 	GObjectClass						*gobjectClass=G_OBJECT_CLASS(klass);
376 	XfdashboardWindowTrackerMonitor		*monitorIface;
377 	GParamSpec							*paramSpec;
378 
379 	/* Reference interface type to lookup properties etc. */
380 	monitorIface=g_type_default_interface_ref(XFDASHBOARD_TYPE_WINDOW_TRACKER_MONITOR);
381 
382 	/* Override functions */
383 	gobjectClass->dispose=_xfdashboard_window_tracker_monitor_x11_dispose;
384 	gobjectClass->set_property=_xfdashboard_window_tracker_monitor_x11_set_property;
385 	gobjectClass->get_property=_xfdashboard_window_tracker_monitor_x11_get_property;
386 
387 	/* Define properties */
388 	paramSpec=g_object_interface_find_property(monitorIface, "is-primary");
389 	XfdashboardWindowTrackerMonitorX11Properties[PROP_IS_PRIMARY]=
390 		g_param_spec_override("is-primary", paramSpec);
391 
392 	paramSpec=g_object_interface_find_property(monitorIface, "monitor-index");
393 	XfdashboardWindowTrackerMonitorX11Properties[PROP_MONITOR_INDEX]=
394 		g_param_spec_override("monitor-index", paramSpec);
395 
396 	g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardWindowTrackerMonitorX11Properties);
397 
398 	/* Release allocated resources */
399 	g_type_default_interface_unref(monitorIface);
400 }
401 
402 /* Object initialization
403  * Create private structure and set up default values
404  */
xfdashboard_window_tracker_monitor_x11_init(XfdashboardWindowTrackerMonitorX11 * self)405 static void xfdashboard_window_tracker_monitor_x11_init(XfdashboardWindowTrackerMonitorX11 *self)
406 {
407 	XfdashboardWindowTrackerMonitorX11Private		*priv;
408 
409 	priv=self->priv=xfdashboard_window_tracker_monitor_x11_get_instance_private(self);
410 
411 	/* Set default values */
412 	priv->monitorIndex=-1;
413 	priv->isPrimary=FALSE;
414 	priv->screen=gdk_screen_get_default();
415 	priv->geometry.x=0;
416 	priv->geometry.y=0;
417 	priv->geometry.width=0;
418 	priv->geometry.height=0;
419 }
420