JWM Source Documentation
dock.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "dock.h"
12 #include "tray.h"
13 #include "main.h"
14 #include "error.h"
15 #include "color.h"
16 #include "misc.h"
17 
18 #define SYSTEM_TRAY_REQUEST_DOCK 0
19 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
20 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
21 
22 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
23 #define SYSTEM_TRAY_ORIENTATION_VERT 1
24 
26 typedef struct DockNode {
27 
28  Window window;
30 
31  struct DockNode *next;
32 
33 } DockNode;
34 
36 typedef struct DockType {
37 
39 
40  Window window;
41  int itemSize;
42 
44 
45 } DockType;
46 
47 static const char BASE_SELECTION_NAME[] = "_NET_SYSTEM_TRAY_S%d";
48 
49 static DockType *dock = NULL;
50 static char owner;
51 static Atom dockAtom;
52 static unsigned long orientation;
53 
54 static void SetSize(TrayComponentType *cp, int width, int height);
55 static void Create(TrayComponentType *cp);
56 static void Resize(TrayComponentType *cp);
57 
58 static void DockWindow(Window win);
59 static void UpdateDock(void);
60 static void GetDockItemSize(int *size);
61 static void GetDockSize(int *width, int *height);
62 
64 void InitializeDock(void)
65 {
66  owner = 0;
67 }
68 
70 void StartupDock(void)
71 {
72 
73  char *selectionName;
74 
75  if(!dock) {
76  /* No dock has been requested. */
77  return;
78  }
79 
80  if(dock->window == None) {
81 
82  /* No dock yet. */
83 
84  /* Get the selection atom. */
85  selectionName = AllocateStack(sizeof(BASE_SELECTION_NAME));
86  snprintf(selectionName, sizeof(BASE_SELECTION_NAME),
88  dockAtom = JXInternAtom(display, selectionName, False);
89  ReleaseStack(selectionName);
90 
91  /* The location and size of the window doesn't matter here. */
93  /* x, y, width, height */ 0, 0, 1, 1,
94  /* border_size, border_color */ 0, 0,
95  /* background */ colors[COLOR_TRAY_BG2]);
97  SubstructureNotifyMask
98  | SubstructureRedirectMask
99  | EnterWindowMask
100  | PointerMotionMask | PointerMotionHintMask);
101 
102  }
103  dock->cp->window = dock->window;
104 
105 }
106 
108 void ShutdownDock(void)
109 {
110 
111  DockNode *np;
112 
113  if(dock) {
114 
115  /* Release memory used by the dock list. */
116  while(dock->nodes) {
117  np = dock->nodes->next;
119  Release(dock->nodes);
120  dock->nodes = np;
121  }
122 
123  /* Release the selection. */
124  if(owner) {
125  JXSetSelectionOwner(display, dockAtom, None, CurrentTime);
126  }
127 
128  /* Destroy the dock window. */
130 
131  }
132 
133 }
134 
136 void DestroyDock(void)
137 {
138  if(dock) {
139  Release(dock);
140  dock = NULL;
141  }
142 }
143 
146 {
147  TrayComponentType *cp;
148 
149  if(JUNLIKELY(dock != NULL && dock->cp != NULL)) {
150  Warning(_("only one Dock allowed"));
151  return NULL;
152  } else if(dock == NULL) {
153  dock = Allocate(sizeof(DockType));
154  dock->nodes = NULL;
155  dock->window = None;
156  }
157 
158  cp = CreateTrayComponent();
159  cp->object = dock;
160  cp->requestedWidth = 1;
161  cp->requestedHeight = 1;
162  dock->cp = cp;
163  dock->itemSize = width;
164 
165  cp->SetSize = SetSize;
166  cp->Create = Create;
167  cp->Resize = Resize;
168 
169  return cp;
170 
171 }
172 
174 void SetSize(TrayComponentType *cp, int width, int height)
175 {
176 
177  Assert(cp);
178  Assert(dock);
179 
180  /* Set the orientation. */
181  if(width == 0) {
183  } else if(height == 0) {
185  }
186 
187  /* Get the size. */
188  cp->width = width;
189  cp->height = height;
190  GetDockSize(&cp->width, &cp->height);
191  if(width == 0) {
192  cp->requestedWidth = cp->width;
193  cp->requestedHeight = 0;
194  } else {
195  cp->requestedWidth = 0;
196  cp->requestedHeight = cp->height;
197  }
198 
199 }
200 
203 {
204 
205  XEvent event;
206 
207  Assert(cp);
208 
209  /* Map the dock window. */
210  if(cp->window != None) {
211  JXResizeWindow(display, cp->window, cp->width, cp->height);
212  JXMapRaised(display, cp->window);
213  }
214 
215  /* Set the orientation atom. */
217  orientation);
218 
219  /* Get the selection if we don't already own it.
220  * If we did already own it, getting it again would cause problems
221  * with some clients due to the way restarts are handled.
222  */
223  if(!owner) {
224 
225  owner = 1;
226  JXSetSelectionOwner(display, dockAtom, dock->cp->window, CurrentTime);
228  != dock->cp->window)) {
229 
230  owner = 0;
231  Warning(_("could not acquire system tray selection"));
232 
233  } else {
234 
235  memset(&event, 0, sizeof(event));
236  event.xclient.type = ClientMessage;
237  event.xclient.window = rootWindow;
238  event.xclient.message_type = atoms[ATOM_MANAGER];
239  event.xclient.format = 32;
240  event.xclient.data.l[0] = CurrentTime;
241  event.xclient.data.l[1] = dockAtom;
242  event.xclient.data.l[2] = dock->cp->window;
243  event.xclient.data.l[3] = 0;
244  event.xclient.data.l[4] = 0;
245 
246  JXSendEvent(display, rootWindow, False, StructureNotifyMask, &event);
247 
248  }
249 
250  }
251 
252 }
253 
256 {
257  JXResizeWindow(display, cp->window, cp->width, cp->height);
258  UpdateDock();
259 }
260 
262 void HandleDockEvent(const XClientMessageEvent *event)
263 {
264  Assert(event);
265  switch(event->data.l[1]) {
267  DockWindow(event->data.l[2]);
268  break;
270  break;
272  break;
273  default:
274  Debug("invalid opcode in dock event");
275  break;
276  }
277 }
278 
280 char HandleDockResizeRequest(const XResizeRequestEvent *event)
281 {
282  DockNode *np;
283 
284  Assert(event);
285 
286  if(!dock) {
287  return 0;
288  }
289 
290  for(np = dock->nodes; np; np = np->next) {
291  if(np->window == event->window) {
292  UpdateDock();
293  return 1;
294  }
295  }
296 
297  return 0;
298 }
299 
301 char HandleDockConfigureRequest(const XConfigureRequestEvent *event)
302 {
303 
304  DockNode *np;
305 
306  Assert(event);
307 
308  if(!dock) {
309  return 0;
310  }
311 
312  for(np = dock->nodes; np; np = np->next) {
313  if(np->window == event->window) {
314  UpdateDock();
315  return 1;
316  }
317  }
318 
319  return 0;
320 
321 }
322 
324 char HandleDockReparentNotify(const XReparentEvent *event)
325 {
326 
327  DockNode *np;
328  char handled;
329 
330  Assert(event);
331 
332  /* Just return if there is no dock. */
333  if(!dock) {
334  return 0;
335  }
336 
337  /* Check each docked window. */
338  handled = 0;
339  for(np = dock->nodes; np; np = np->next) {
340  if(np->window == event->window) {
341  if(event->parent != dock->cp->window) {
342  /* For some reason the application reparented the window.
343  * We make note of this condition and reparent every time
344  * the dock is updated. Unfortunately we can't do this for
345  * all applications because some won't deal with it.
346  */
347  np->needs_reparent = 1;
348  handled = 1;
349  }
350  }
351  }
352 
353  /* Layout the stuff on the dock again if something happened. */
354  if(handled) {
355  UpdateDock();
356  }
357 
358  return handled;
359 
360 }
361 
363 char HandleDockSelectionClear(const XSelectionClearEvent *event)
364 {
365  if(event->selection == dockAtom) {
366  Debug("lost _NET_SYSTEM_TRAY selection");
367  owner = 0;
368  }
369  return 0;
370 }
371 
373 void DockWindow(Window win)
374 {
375  DockNode *np;
376 
377  /* If no dock is running, just return. */
378  if(!dock) {
379  return;
380  }
381 
382  /* Make sure we have a valid window to add. */
383  if(JUNLIKELY(win == None)) {
384  return;
385  }
386 
387  /* If this window is already docked ignore it. */
388  for(np = dock->nodes; np; np = np->next) {
389  if(np->window == win) {
390  return;
391  }
392  }
393 
394  /* Add the window to our list. */
395  np = Allocate(sizeof(DockNode));
396  np->window = win;
397  np->needs_reparent = 0;
398  np->next = dock->nodes;
399  dock->nodes = np;
400 
401  /* Update the requested size. */
402  GetDockSize(&dock->cp->requestedWidth, &dock->cp->requestedHeight);
403 
404  /* It's safe to reparent at (0, 0) since we call
405  * ResizeTray which will invoke the Resize callback.
406  */
407  JXAddToSaveSet(display, win);
408  JXReparentWindow(display, win, dock->cp->window, 0, 0);
409  JXMapRaised(display, win);
410 
411  /* Resize the tray containing the dock. */
412  ResizeTray(dock->cp->tray);
413 
414 }
415 
417 char HandleDockDestroy(Window win)
418 {
419  DockNode **np;
420 
421  /* If no dock is running, just return. */
422  if(!dock) {
423  return 0;
424  }
425 
426  for(np = &dock->nodes; *np; np = &(*np)->next) {
427  DockNode *dp = *np;
428  if(dp->window == win) {
429 
430  /* Remove the window from our list. */
431  *np = dp->next;
432  Release(dp);
433 
434  /* Update the requested size. */
435  GetDockSize(&dock->cp->requestedWidth, &dock->cp->requestedHeight);
436 
437  /* Resize the tray. */
438  ResizeTray(dock->cp->tray);
439  return 1;
440  }
441  }
442 
443  return 0;
444 }
445 
447 void UpdateDock(void)
448 {
449 
450  XConfigureEvent event;
451  DockNode *np;
452  int x, y;
453  int itemSize;
454 
455  Assert(dock);
456 
457  /* Determine the size of items in the dock. */
458  GetDockItemSize(&itemSize);
459 
460  x = 0;
461  y = 0;
462  memset(&event, 0, sizeof(event));
463  for(np = dock->nodes; np; np = np->next) {
464 
465  JXMoveResizeWindow(display, np->window, x, y, itemSize, itemSize);
466 
467  /* Reparent if this window likes to go other places. */
468  if(np->needs_reparent) {
469  JXReparentWindow(display, np->window, dock->cp->window, x, y);
470  }
471 
472  event.type = ConfigureNotify;
473  event.event = np->window;
474  event.window = np->window;
475  event.x = dock->cp->screenx + x;
476  event.y = dock->cp->screeny + y;
477  event.width = itemSize;
478  event.height = itemSize;
479  JXSendEvent(display, np->window, False, StructureNotifyMask,
480  (XEvent*)&event);
481 
483  x += itemSize;
484  } else {
485  y += itemSize;
486  }
487 
488  }
489 
490 }
491 
493 void GetDockItemSize(int *size)
494 {
495  /* Determine the default size of items in the dock. */
497  *size = dock->cp->height;
498  } else {
499  *size = dock->cp->width;
500  }
501  if(dock->itemSize > 0 && *size > dock->itemSize) {
502  *size = dock->itemSize;
503  }
504 }
505 
507 void GetDockSize(int *width, int *height)
508 {
509  DockNode *np;
510  int itemSize;
511 
512  Assert(dock != NULL);
513 
514  /* Get the dock item size. */
515  GetDockItemSize(&itemSize);
516 
517  /* Determine the size of the items on the dock. */
518  for(np = dock->nodes; np; np = np->next) {
520  /* Horizontal tray; height fixed, placement is left to right. */
521  *width += itemSize;
522  } else {
523  /* Vertical tray; width fixed, placement is top to bottom. */
524  *height += itemSize;
525  }
526  }
527 
528  /* Don't allow the dock to have zero size since a size of
529  * zero indicates a variable sized component. */
531  *width = Max(*width, 1);
532  } else {
533  *height = Max(*height, 1);
534  }
535 
536 }

joewing.net / Projects / JWM