1 /* workspace object */
2 /* vim: set sw=2 et: */
3 
4 /*
5  * Copyright (C) 2001 Havoc Pennington
6  * Copyright (C) 2003 Kim Woelders
7  * Copyright (C) 2003 Red Hat, Inc.
8  * Copyright (C) 2006-2007 Vincent Untz
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25 
26 #include <config.h>
27 
28 #include <glib/gi18n-lib.h>
29 #include "workspace.h"
30 #include "xutils.h"
31 #include "private.h"
32 #include <string.h>
33 
34 /**
35  * SECTION:workspace
36  * @short_description: an object representing a workspace.
37  * @see_also: #WnckScreen
38  * @stability: Unstable
39  *
40  * The #WnckWorkspace represents what is called <ulink
41  * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html&num;largedesks">virtual
42  * desktops</ulink> in the <ulink
43  * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html">EWMH</ulink>.
44  * A workspace is a virtualization of a #WnckScreen<!-- -->: only one workspace
45  * can be shown on a #WnckScreen at a time. It makes it possible, for example,
46  * to put windows on different workspaces to organize them.
47  *
48  * If the #WnckWorkspace size is bigger that the #WnckScreen size, the
49  * workspace contains a viewport. Viewports are defined in the <ulink
50  * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html&num;id2457064">large
51  * desktops</ulink> section of the <ulink
52  * url="http://standards.freedesktop.org/wm-spec/wm-spec-latest.html">EWMH</ulink>.
53  * The notion of workspaces and viewports are quite similar, and generally both
54  * are not used at the same time: there are generally either multiple
55  * workspaces with no viewport, or one workspace with a viewport. libwnck
56  * supports all situations, even multiple workspaces with viewports.
57  *
58  * Workspaces are organized according to a layout set on the #WnckScreen. See
59  * wnck_screen_try_set_workspace_layout() and
60  * wnck_screen_release_workspace_layout() for more information about the
61  * layout.
62  *
63  * The #WnckWorkspace objects are always owned by libwnck and must not be
64  * referenced or unreferenced.
65  */
66 
67 struct _WnckWorkspacePrivate
68 {
69   WnckScreen *screen;
70   int number;
71   char *name;
72   int width, height;            /* Workspace size */
73   int viewport_x, viewport_y;   /* Viewport origin */
74   gboolean is_virtual;
75 };
76 
77 G_DEFINE_TYPE (WnckWorkspace, wnck_workspace, G_TYPE_OBJECT);
78 #define WNCK_WORKSPACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WNCK_TYPE_WORKSPACE, WnckWorkspacePrivate))
79 
80 enum {
81   NAME_CHANGED,
82   LAST_SIGNAL
83 };
84 
85 static void wnck_workspace_init        (WnckWorkspace      *workspace);
86 static void wnck_workspace_class_init  (WnckWorkspaceClass *klass);
87 static void wnck_workspace_finalize    (GObject        *object);
88 
89 
90 static void emit_name_changed (WnckWorkspace *space);
91 
92 static guint signals[LAST_SIGNAL] = { 0 };
93 
94 static void
wnck_workspace_init(WnckWorkspace * workspace)95 wnck_workspace_init (WnckWorkspace *workspace)
96 {
97   workspace->priv = WNCK_WORKSPACE_GET_PRIVATE (workspace);
98 
99   workspace->priv->screen = NULL;
100   workspace->priv->number = -1;
101   workspace->priv->name = NULL;
102   workspace->priv->width = 0;
103   workspace->priv->height = 0;
104   workspace->priv->viewport_x = 0;
105   workspace->priv->viewport_y = 0;
106   workspace->priv->is_virtual = FALSE;
107 }
108 
109 static void
wnck_workspace_class_init(WnckWorkspaceClass * klass)110 wnck_workspace_class_init (WnckWorkspaceClass *klass)
111 {
112   GObjectClass *object_class = G_OBJECT_CLASS (klass);
113 
114   g_type_class_add_private (klass, sizeof (WnckWorkspacePrivate));
115 
116   object_class->finalize = wnck_workspace_finalize;
117 
118   /**
119    * WnckWorkspace::name-changed:
120    * @space: the #WnckWorkspace which emitted the signal.
121    *
122    * Emitted when the name of @space changes.
123    */
124   signals[NAME_CHANGED] =
125     g_signal_new ("name_changed",
126                   G_OBJECT_CLASS_TYPE (object_class),
127                   G_SIGNAL_RUN_LAST,
128                   G_STRUCT_OFFSET (WnckWorkspaceClass, name_changed),
129                   NULL, NULL,
130                   g_cclosure_marshal_VOID__VOID,
131                   G_TYPE_NONE, 0);
132 }
133 
134 static void
wnck_workspace_finalize(GObject * object)135 wnck_workspace_finalize (GObject *object)
136 {
137   WnckWorkspace *workspace;
138 
139   workspace = WNCK_WORKSPACE (object);
140 
141   g_free (workspace->priv->name);
142   workspace->priv->name = NULL;
143 
144   G_OBJECT_CLASS (wnck_workspace_parent_class)->finalize (object);
145 }
146 
147 /**
148  * wnck_workspace_get_number:
149  * @space: a #WnckWorkspace.
150  *
151  * Gets the index of @space on the #WnckScreen to which it belongs. The
152  * first workspace has an index of 0.
153  *
154  * Return value: the index of @space on its #WnckScreen, or -1 on errors.
155  **/
156 int
wnck_workspace_get_number(WnckWorkspace * space)157 wnck_workspace_get_number (WnckWorkspace *space)
158 {
159   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), -1);
160 
161   return space->priv->number;
162 }
163 
164 /**
165  * wnck_workspace_get_name:
166  * @space: a #WnckWorkspace.
167  *
168  * Gets the human-readable name that should be used to refer to @space. If
169  * the user has not set a special name, a fallback like "Workspace 3" will be
170  * used.
171  *
172  * Return value: the name of @space.
173  **/
174 const char*
wnck_workspace_get_name(WnckWorkspace * space)175 wnck_workspace_get_name (WnckWorkspace *space)
176 {
177   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), NULL);
178 
179   return space->priv->name;
180 }
181 
182 /**
183  * wnck_workspace_change_name:
184  * @space: a #WnckWorkspace.
185  * @name: new name for @space.
186  *
187  * Changes the name of @space.
188  *
189  * Since: 2.2
190  **/
191 void
wnck_workspace_change_name(WnckWorkspace * space,const char * name)192 wnck_workspace_change_name (WnckWorkspace *space,
193                             const char    *name)
194 {
195   g_return_if_fail (WNCK_IS_WORKSPACE (space));
196   g_return_if_fail (name != NULL);
197 
198   _wnck_screen_change_workspace_name (space->priv->screen,
199                                       space->priv->number,
200                                       name);
201 }
202 
203 /**
204  * wnck_workspace_get_screen:
205  * @space: a #WnckWorkspace.
206  *
207  * Gets the #WnckScreen @space is on.
208  *
209  * Return value: the #WnckScreen @space is on. The returned #WnckScreen is
210  * owned by libwnck and must not be referenced or unreferenced.
211  **/
212 WnckScreen*
wnck_workspace_get_screen(WnckWorkspace * space)213 wnck_workspace_get_screen (WnckWorkspace *space)
214 {
215   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), NULL);
216 
217   return space->priv->screen;
218 }
219 
220 /**
221  * wnck_workspace_activate:
222  * @space: a #WnckWorkspace.
223  * @timestamp: the X server timestamp of the user interaction event that caused
224  * this call to occur.
225  *
226  * Asks the window manager to make @space the active workspace. The window
227  * manager may decide to refuse the request (to not steal the focus if there is
228  * a more recent user activity, for example).
229  *
230  * This function existed before 2.10, but the @timestamp argument was missing
231  * in earlier versions.
232  *
233  * Since: 2.10
234  **/
235 void
wnck_workspace_activate(WnckWorkspace * space,guint32 timestamp)236 wnck_workspace_activate (WnckWorkspace *space,
237                          guint32        timestamp)
238 {
239   g_return_if_fail (WNCK_IS_WORKSPACE (space));
240 
241   _wnck_activate_workspace (WNCK_SCREEN_XSCREEN (space->priv->screen),
242                             space->priv->number,
243                             timestamp);
244 }
245 
246 WnckWorkspace*
_wnck_workspace_create(int number,WnckScreen * screen)247 _wnck_workspace_create (int number, WnckScreen *screen)
248 {
249   WnckWorkspace *space;
250 
251   space = g_object_new (WNCK_TYPE_WORKSPACE, NULL);
252   space->priv->number = number;
253   space->priv->name = NULL;
254   space->priv->screen = screen;
255 
256   _wnck_workspace_update_name (space, NULL);
257 
258   /* Just set reasonable defaults */
259   space->priv->width = wnck_screen_get_width (screen);
260   space->priv->height = wnck_screen_get_height (screen);
261   space->priv->is_virtual = FALSE;
262 
263   space->priv->viewport_x = 0;
264   space->priv->viewport_y = 0;
265 
266   return space;
267 }
268 
269 void
_wnck_workspace_update_name(WnckWorkspace * space,const char * name)270 _wnck_workspace_update_name (WnckWorkspace *space,
271                              const char    *name)
272 {
273   char *old;
274 
275   g_return_if_fail (WNCK_IS_WORKSPACE (space));
276 
277   old = space->priv->name;
278   space->priv->name = g_strdup (name);
279 
280   if (space->priv->name == NULL)
281     space->priv->name = g_strdup_printf (_("Workspace %d"),
282                                          space->priv->number + 1);
283 
284   if ((old && !name) ||
285       (!old && name) ||
286       (old && name && strcmp (old, name) != 0))
287     emit_name_changed (space);
288 
289   g_free (old);
290 }
291 
292 static void
emit_name_changed(WnckWorkspace * space)293 emit_name_changed (WnckWorkspace *space)
294 {
295   g_signal_emit (G_OBJECT (space),
296                  signals[NAME_CHANGED],
297                  0);
298 }
299 
300 gboolean
_wnck_workspace_set_geometry(WnckWorkspace * space,int w,int h)301 _wnck_workspace_set_geometry (WnckWorkspace *space,
302                               int            w,
303                               int            h)
304 {
305   if (space->priv->width != w || space->priv->height != h)
306     {
307       space->priv->width = w;
308       space->priv->height = h;
309 
310       space->priv->is_virtual = w > wnck_screen_get_width (space->priv->screen) ||
311 				h > wnck_screen_get_height (space->priv->screen);
312 
313       return TRUE;  /* change was made */
314     }
315   else
316     return FALSE;
317 }
318 
319 gboolean
_wnck_workspace_set_viewport(WnckWorkspace * space,int x,int y)320 _wnck_workspace_set_viewport (WnckWorkspace *space,
321                               int            x,
322                               int            y)
323 {
324   if (space->priv->viewport_x != x || space->priv->viewport_y != y)
325     {
326       space->priv->viewport_x = x;
327       space->priv->viewport_y = y;
328 
329       return TRUE; /* change was made */
330     }
331   else
332     return FALSE;
333 }
334 
335 /**
336  * wnck_workspace_get_width:
337  * @space: a #WnckWorkspace.
338  *
339  * Gets the width of @space.
340  *
341  * Returns: the width of @space.
342  *
343  * Since: 2.4
344  */
345 int
wnck_workspace_get_width(WnckWorkspace * space)346 wnck_workspace_get_width (WnckWorkspace *space)
347 {
348   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), 0);
349 
350   return space->priv->width;
351 }
352 
353 /**
354  * wnck_workspace_get_height:
355  * @space: a #WnckWorkspace.
356  *
357  * Gets the height of @space.
358  *
359  * Returns: the height of @space.
360  *
361  * Since: 2.4
362  */
363 int
wnck_workspace_get_height(WnckWorkspace * space)364 wnck_workspace_get_height (WnckWorkspace *space)
365 {
366   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), 0);
367 
368   return space->priv->height;
369 }
370 
371 /**
372  * wnck_workspace_get_viewport_x:
373  * @space: a #WnckWorkspace.
374  *
375  * Gets the X coordinate of the viewport in @space.
376  *
377  * Returns: the X coordinate of the viewport in @space, or 0 if @space does not
378  * contain a viewport.
379  *
380  * Since: 2.4
381  */
382 int
wnck_workspace_get_viewport_x(WnckWorkspace * space)383 wnck_workspace_get_viewport_x (WnckWorkspace *space)
384 {
385   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), 0);
386 
387   return space->priv->viewport_x;
388 }
389 
390 /**
391  * wnck_workspace_get_viewport_y:
392  * @space: a #WnckWorkspace.
393  *
394  * Gets the Y coordinate of the viewport in @space.
395  *
396  * Returns: the Y coordinate of the viewport in @space, or 0 if @space does not
397  * contain a viewport.
398  *
399  * Since: 2.4
400  */
401 int
wnck_workspace_get_viewport_y(WnckWorkspace * space)402 wnck_workspace_get_viewport_y (WnckWorkspace *space)
403 {
404   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), 0);
405 
406   return space->priv->viewport_y;
407 }
408 
409 /**
410  * wnck_workspace_is_virtual:
411  * @space: a #WnckWorkspace.
412  *
413  * Gets whether @space contains a viewport.
414  *
415  * Returns: %TRUE if @space contains a viewport, %FALSE otherwise.
416  *
417  * Since: 2.4
418  */
419 gboolean
wnck_workspace_is_virtual(WnckWorkspace * space)420 wnck_workspace_is_virtual (WnckWorkspace *space)
421 {
422   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), FALSE);
423 
424   return space->priv->is_virtual;
425 }
426 
427 /**
428  * wnck_workspace_get_layout_row:
429  * @space: a #WnckWorkspace.
430  *
431  * Gets the row of @space in the #WnckWorkspace layout. The first row has an
432  * index of 0 and is always the top row, regardless of the starting corner set
433  * for the layout.
434  *
435  * Return value: the row of @space in the #WnckWorkspace layout, or -1 on
436  * errors.
437  *
438  * Since: 2.20
439  **/
440 int
wnck_workspace_get_layout_row(WnckWorkspace * space)441 wnck_workspace_get_layout_row (WnckWorkspace *space)
442 {
443   _WnckLayoutOrientation orientation;
444   _WnckLayoutCorner corner;
445   int n_rows;
446   int n_cols;
447   int row;
448 
449   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), -1);
450 
451   _wnck_screen_get_workspace_layout (space->priv->screen,
452                                      &orientation, &n_rows, &n_cols, &corner);
453 
454   if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
455     row = space->priv->number / n_cols;
456   else
457     row = space->priv->number % n_rows;
458 
459   if (corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT ||
460       corner == WNCK_LAYOUT_CORNER_BOTTOMLEFT)
461     row = n_rows - row;
462 
463   return row;
464 }
465 
466 /**
467  * wnck_workspace_get_layout_column:
468  * @space: a #WnckWorkspace.
469  *
470  * Gets the column of @space in the #WnckWorkspace layout. The first column
471  * has an index of 0 and is always the left column, regardless of the starting
472  * corner set for the layout and regardless of the default direction of the
473  * environment (i.e., in both Left-To-Right and Right-To-Left environments).
474  *
475  * Return value: the column of @space in the #WnckWorkspace layout, or -1 on
476  * errors.
477  *
478  * Since: 2.20
479  **/
480 int
wnck_workspace_get_layout_column(WnckWorkspace * space)481 wnck_workspace_get_layout_column (WnckWorkspace *space)
482 {
483   _WnckLayoutOrientation orientation;
484   _WnckLayoutCorner corner;
485   int n_rows;
486   int n_cols;
487   int col;
488 
489   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), -1);
490 
491   _wnck_screen_get_workspace_layout (space->priv->screen,
492                                      &orientation, &n_rows, &n_cols, &corner);
493 
494   if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
495     col = space->priv->number % n_cols;
496   else
497     col = space->priv->number / n_rows;
498 
499   if (corner == WNCK_LAYOUT_CORNER_TOPRIGHT ||
500       corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT)
501     col = n_cols - col;
502 
503   return col;
504 }
505 
506 /**
507  * wnck_workspace_get_neighbor:
508  * @space: a #WnckWorkspace.
509  * @direction: direction in which to search the neighbor.
510  *
511  * Gets the neighbor #WnckWorkspace of @space in the @direction direction.
512  *
513  * Return value: the neighbor #WnckWorkspace of @space in the @direction
514  * direction, or %NULL if no such neighbor #WnckWorkspace exists. The returned
515  * #WnckWorkspace is owned by libwnck and must not be referenced or
516  * unreferenced.
517  *
518  * Since: 2.20
519  **/
520 WnckWorkspace*
wnck_workspace_get_neighbor(WnckWorkspace * space,WnckMotionDirection direction)521 wnck_workspace_get_neighbor (WnckWorkspace       *space,
522                              WnckMotionDirection  direction)
523 {
524   _WnckLayoutOrientation orientation;
525   _WnckLayoutCorner corner;
526   int n_rows;
527   int n_cols;
528   int row;
529   int col;
530   int add;
531   int index;
532 
533   g_return_val_if_fail (WNCK_IS_WORKSPACE (space), NULL);
534 
535   _wnck_screen_get_workspace_layout (space->priv->screen,
536                                      &orientation, &n_rows, &n_cols, &corner);
537 
538   row = wnck_workspace_get_layout_row (space);
539   col = wnck_workspace_get_layout_column (space);
540 
541   index = space->priv->number;
542 
543   switch (direction)
544     {
545     case WNCK_MOTION_LEFT:
546       if (col == 0)
547         return NULL;
548 
549       if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
550         add = 1;
551       else
552         add = n_rows;
553 
554       if (corner == WNCK_LAYOUT_CORNER_TOPRIGHT ||
555           corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT)
556         index += add;
557       else
558         index -= add;
559       break;
560 
561     case WNCK_MOTION_RIGHT:
562       if (col == n_cols - 1)
563         return NULL;
564 
565       if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
566         add = 1;
567       else
568         add = n_rows;
569 
570       if (corner == WNCK_LAYOUT_CORNER_TOPRIGHT ||
571           corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT)
572         index -= add;
573       else
574         index += add;
575       break;
576 
577     case WNCK_MOTION_UP:
578       if (row == 0)
579         return NULL;
580 
581       if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
582         add = n_cols;
583       else
584         add = 1;
585 
586       if (corner == WNCK_LAYOUT_CORNER_BOTTOMLEFT ||
587           corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT)
588         index += add;
589       else
590         index -= add;
591       break;
592 
593     case WNCK_MOTION_DOWN:
594       if (row == n_rows - 1)
595         return NULL;
596 
597       if (orientation == WNCK_LAYOUT_ORIENTATION_HORIZONTAL)
598         add = n_cols;
599       else
600         add = 1;
601 
602       if (corner == WNCK_LAYOUT_CORNER_BOTTOMLEFT ||
603           corner == WNCK_LAYOUT_CORNER_BOTTOMRIGHT)
604         index -= add;
605       else
606         index += add;
607       break;
608     }
609 
610   if (index == space->priv->number)
611     return NULL;
612 
613   return wnck_screen_get_workspace (space->priv->screen, index);
614 }
615