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#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#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