JWM Source Documentation
pager.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "pager.h"
12 
13 #include "client.h"
14 #include "clientlist.h"
15 #include "color.h"
16 #include "cursor.h"
17 #include "desktop.h"
18 #include "event.h"
19 #include "tray.h"
20 #include "timing.h"
21 #include "popup.h"
22 #include "font.h"
23 #include "settings.h"
24 
26 typedef struct PagerType {
27 
30  int deskWidth;
31  int deskHeight;
32  int scalex;
33  int scaley;
34  char labeled;
36  Pixmap buffer;
39  int mousex, mousey;
41  struct PagerType *next;
43 } PagerType;
44 
45 static PagerType *pagers = NULL;
46 
47 static char shouldStopMove;
48 
49 static void Create(TrayComponentType *cp);
50 
51 static void SetSize(TrayComponentType *cp, int width, int height);
52 
53 static int GetPagerDesktop(PagerType *pp, int x, int y);
54 
56  int x, int y, int mask);
57 
59  int x, int y, int mask);
60 
61 static void StartPagerMove(TrayComponentType *cp, int x, int y);
62 
63 static void StopPagerMove(ClientNode *np,
64  int x, int y, int desktop, MaxFlags maxFlags);
65 
66 static void PagerMoveController(int wasDestroyed);
67 
68 static void DrawPager(const PagerType *pp);
69 
70 static void DrawPagerClient(const PagerType *pp, const ClientNode *np);
71 
72 static void SignalPager(const TimeType *now, int x, int y, Window w,
73  void *data);
74 
75 
77 void ShutdownPager(void)
78 {
79  PagerType *pp;
80  for(pp = pagers; pp; pp = pp->next) {
82  }
83 }
84 
86 void DestroyPager(void)
87 {
88  PagerType *pp;
89  while(pagers) {
91  pp = pagers->next;
92  Release(pagers);
93  pagers = pp;
94  }
95 }
96 
99 {
100 
102  PagerType *pp;
103 
104  pp = Allocate(sizeof(PagerType));
105  pp->next = pagers;
106  pagers = pp;
107  pp->labeled = labeled;
110  pp->mouseTime.seconds = 0;
111  pp->mouseTime.ms = 0;
112  pp->buffer = None;
113 
114  cp = CreateTrayComponent();
115  cp->object = pp;
116  pp->cp = cp;
117  cp->Create = Create;
118  cp->SetSize = SetSize;
121 
123 
124  return cp;
125 }
126 
129 {
130 
131  PagerType *pp;
132 
133  Assert(cp);
134 
135  pp = (PagerType*)cp->object;
136 
137  Assert(pp);
138 
139  Assert(cp->width > 0);
140  Assert(cp->height > 0);
141 
143  cp->height, rootDepth);
144  pp->buffer = cp->pixmap;
145 
146 }
147 
149 void SetSize(TrayComponentType *cp, int width, int height)
150 {
151 
152  PagerType *pp = (PagerType*)cp->object;
153 
154  if(width) {
155 
156  /* Vertical pager. */
157  cp->width = width;
158 
159  pp->deskWidth = width / settings.desktopWidth;
160  pp->deskHeight = (pp->deskWidth * rootHeight) / rootWidth;
161 
163  + settings.desktopHeight - 1;
164 
165  } else if(height) {
166 
167  /* Horizontal pager. */
168  cp->height = height;
169 
170  pp->deskHeight = height / settings.desktopHeight;
171  pp->deskWidth = (pp->deskHeight * rootWidth) / rootHeight;
172 
174  + settings.desktopWidth - 1;
175 
176  } else {
177  Assert(0);
178  }
179 
180  if(pp->buffer != None) {
183  cp->height, rootDepth);
184  cp->pixmap = pp->buffer;
185  DrawPager(pp);
186  }
187 
188  pp->scalex = ((pp->deskWidth - 2) << 16) / rootWidth;
189  pp->scaley = ((pp->deskHeight - 2) << 16) / rootHeight;
190 
191 }
192 
194 int GetPagerDesktop(PagerType *pp, int x, int y)
195 {
196 
197  int pagerx, pagery;
198 
199  pagerx = x / (pp->deskWidth + 1);
200  pagery = y / (pp->deskHeight + 1);
201 
202  return pagery * settings.desktopWidth + pagerx;
203 
204 }
205 
207 void ProcessPagerButtonEvent(TrayComponentType *cp, int x, int y, int mask)
208 {
209 
210  PagerType *pp;
211 
212  switch(mask) {
213  case Button1:
214  case Button2:
215 
216  /* Change to the selected desktop. */
217  pp = (PagerType*)cp->object;
218  ChangeDesktop(GetPagerDesktop(pp, x, y));
219  break;
220 
221  case Button3:
222 
223  /* Move a client and possibly change its desktop. */
224  StartPagerMove(cp, x, y);
225  break;
226 
227  case Button4:
228 
229  /* Change to the previous desktop. */
230  LeftDesktop();
231  break;
232 
233  case Button5:
234 
235  /* Change to the next desktop. */
236  RightDesktop();
237  break;
238 
239  default:
240  break;
241  }
242 }
243 
245 void ProcessPagerMotionEvent(TrayComponentType *cp, int x, int y, int mask)
246 {
247 
248  PagerType *pp = (PagerType*)cp->object;
249 
250  pp->mousex = cp->screenx + x;
251  pp->mousey = cp->screeny + y;
253 }
254 
256 void StartPagerMove(TrayComponentType *cp, int x, int y)
257 {
258 
259  XEvent event;
260  PagerType *pp;
261  ClientNode *np;
262  int layer;
263  int desktop;
264  int cx, cy;
265  int cwidth, cheight;
266 
267  int north, south, east, west;
268  int oldx, oldy;
269  int oldDesk;
270  int startx, starty;
271  MaxFlags maxFlags;
272 
273  pp = (PagerType*)cp->object;
274 
275  /* Determine the selected desktop. */
276  desktop = GetPagerDesktop(pp, x, y);
277  x -= (desktop % settings.desktopWidth) * (pp->deskWidth + 1);
278  y -= (desktop / settings.desktopWidth) * (pp->deskHeight + 1);
279 
280  /* Find the client under the specified coordinates. */
281  for(layer = LAST_LAYER; layer >= FIRST_LAYER; layer--) {
282  for(np = nodes[layer]; np; np = np->next) {
283 
284  /* Skip this client if it isn't mapped. */
285  if(!(np->state.status & STAT_MAPPED)) {
286  continue;
287  }
288  if(np->state.status & STAT_NOPAGER) {
289  continue;
290  }
291 
292  /* Skip this client if it isn't on the selected desktop. */
293  if(np->state.status & STAT_STICKY) {
294  if(currentDesktop != desktop) {
295  continue;
296  }
297  } else {
298  if(np->state.desktop != desktop) {
299  continue;
300  }
301  }
302 
303  /* Get the offset and size of the client on the pager. */
304  cx = 1 + ((np->x * pp->scalex) >> 16);
305  cy = 1 + ((np->y * pp->scaley) >> 16);
306  cwidth = (np->width * pp->scalex) >> 16;
307  cheight = (np->height * pp->scaley) >> 16;
308 
309  /* Normalize the offset and size. */
310  if(cx + cwidth > pp->deskWidth) {
311  cwidth = pp->deskWidth - cx;
312  }
313  if(cy + cheight > pp->deskHeight) {
314  cheight = pp->deskHeight - cy;
315  }
316  if(cx < 0) {
317  cwidth += cx;
318  cx = 0;
319  }
320  if(cy < 0) {
321  cheight += cy;
322  cy = 0;
323  }
324 
325  /* Skip the client if we are no longer in bounds. */
326  if(cwidth <= 0 || cheight <= 0) {
327  continue;
328  }
329 
330  /* Check the y-coordinate. */
331  if(y < cy || y > cy + cheight) {
332  continue;
333  }
334 
335  /* Check the x-coordinate. */
336  if(x < cx || x > cx + cwidth) {
337  continue;
338  }
339 
340  /* Found it. Exit. */
341  goto ClientFound;
342 
343  }
344  }
345 
346  /* Client wasn't found. Just return. */
347  return;
348 
349 ClientFound:
350 
351  Assert(np);
352 
353  /* The selected client was found. Now make sure we can move it. */
354  if(!(np->state.border & BORDER_MOVE)) {
355  return;
356  }
357 
358  /* Start the move. */
359  if(!GrabMouseForMove()) {
360  return;
361  }
362 
363  /* If the client is maximized, unmaximize it. */
364  maxFlags = np->state.maxFlags;
365  if(np->state.maxFlags) {
367  }
368 
369  GetBorderSize(&np->state, &north, &south, &east, &west);
370 
372  shouldStopMove = 0;
373 
374  oldx = np->x;
375  oldy = np->y;
376  oldDesk = np->state.desktop;
377 
378  startx = x;
379  starty = y;
380 
381  if(!(GetMouseMask() & Button3Mask)) {
382  StopPagerMove(np, oldx, oldy, oldDesk, maxFlags);
383  }
384 
385  for(;;) {
386 
387  WaitForEvent(&event);
388 
389  if(shouldStopMove) {
390  np->controller = NULL;
391  return;
392  }
393 
394  switch(event.type) {
395  case ButtonRelease:
396 
397  /* Done when the 3rd mouse button is released. */
398  if(event.xbutton.button == Button3) {
399  StopPagerMove(np, oldx, oldy, oldDesk, maxFlags);
400  return;
401  }
402  break;
403 
404  case MotionNotify:
405 
406  SetMousePosition(event.xmotion.x_root, event.xmotion.y_root,
407  event.xmotion.window);
408 
409  /* Get the mouse position on the pager. */
410  x = event.xmotion.x_root - cp->screenx;
411  y = event.xmotion.y_root - cp->screeny;
412 
413  /* Don't move if we are off of the pager. */
414  if(x < 0 || x > cp->width) {
415  break;
416  }
417  if(y < 0 || y > cp->height) {
418  break;
419  }
420 
421  /* Determine the new client desktop. */
422  desktop = GetPagerDesktop(pp, x, y);
423  x -= pp->deskWidth * (desktop % settings.desktopWidth);
424  y -= pp->deskHeight * (desktop / settings.desktopWidth);
425 
426  /* If this client isn't sticky and now on a different desktop
427  * change the client's desktop. */
428  if(!(np->state.status & STAT_STICKY)) {
429  if(desktop != oldDesk) {
430  SetClientDesktop(np, (unsigned int)desktop);
431  oldDesk = desktop;
432  }
433  }
434 
435  /* Get new client coordinates. */
436  oldx = startx + (x - startx);
437  oldx = (oldx << 16) / pp->scalex;
438  oldx -= (np->width + east + west) / 2;
439  oldy = starty + (y - starty);
440  oldy = (oldy << 16) / pp->scaley;
441  oldy -= (np->height + north + south) / 2;
442 
443  /* Move the window. */
444  np->x = oldx;
445  np->y = oldy;
446  JXMoveWindow(display, np->parent, np->x - west, np->y - north);
447  SendConfigureEvent(np);
449 
450  break;
451 
452  default:
453  break;
454  }
455 
456  }
457 
458 }
459 
462  int x, int y, int desktop, MaxFlags maxFlags)
463 {
464 
465  int north, south, east, west;
466 
467  Assert(np);
468  Assert(np->controller);
469 
470  /* Release grabs. */
471  (np->controller)(0);
472 
473  np->x = x;
474  np->y = y;
475 
476  GetBorderSize(&np->state, &north, &south, &east, & west);
477  JXMoveWindow(display, np->parent, np->x - west, np->y - north);
478  SendConfigureEvent(np);
479 
480  /* Restore the maximized state of the client. */
481  if(maxFlags != MAX_NONE) {
482  MaximizeClient(np, maxFlags);
483  }
484 
485  /* Redraw the pager. */
487 
488 }
489 
491 void PagerMoveController(int wasDestroyed)
492 {
493 
494  JXUngrabPointer(display, CurrentTime);
495  JXUngrabKeyboard(display, CurrentTime);
496  shouldStopMove = 1;
497 
498 }
499 
501 void DrawPager(const PagerType *pp)
502 {
503  ClientNode *np;
504  Pixmap buffer;
505  int width, height;
506  int deskWidth, deskHeight;
507  unsigned int x;
508  const char *name;
509  int xc, yc;
510  int textWidth, textHeight;
511  int dx, dy;
512 
513  buffer = pp->cp->pixmap;
514  width = pp->cp->width;
515  height = pp->cp->height;
516  deskWidth = pp->deskWidth;
517  deskHeight = pp->deskHeight;
518 
519  /* Draw the background. */
521  JXFillRectangle(display, buffer, rootGC, 0, 0, width, height);
522 
523  /* Highlight the current desktop. */
527  JXFillRectangle(display, buffer, rootGC,
528  dx * (deskWidth + 1), dy * (deskHeight + 1),
529  deskWidth, deskHeight);
530 
531  /* Draw the labels. */
532  if(pp->labeled) {
533  textHeight = GetStringHeight(FONT_PAGER);
534  if(textHeight < deskHeight) {
535  for(x = 0; x < settings.desktopCount; x++) {
536  dx = x % settings.desktopWidth;
537  dy = x / settings.desktopWidth;
538  name = GetDesktopName(x);
539  textWidth = GetStringWidth(FONT_PAGER, name);
540  if(textWidth < deskWidth) {
541  xc = dx * (deskWidth + 1) + (deskWidth - textWidth) / 2;
542  yc = dy * (deskHeight + 1) + (deskHeight - textHeight) / 2;
543  RenderString(buffer, FONT_PAGER,
544  COLOR_PAGER_TEXT, xc, yc, deskWidth, name);
545  }
546  }
547  }
548  }
549 
550  /* Draw the clients. */
551  for(x = FIRST_LAYER; x <= LAST_LAYER; x++) {
552  for(np = nodeTail[x]; np; np = np->prev) {
553  DrawPagerClient(pp, np);
554  }
555  }
556 
557  /* Draw the desktop dividers. */
559  for(x = 1; x < settings.desktopHeight; x++) {
560  JXDrawLine(display, buffer, rootGC,
561  0, (deskHeight + 1) * x - 1,
562  width, (deskHeight + 1) * x - 1);
563  }
564  for(x = 1; x < settings.desktopWidth; x++) {
565  JXDrawLine(display, buffer, rootGC,
566  (deskWidth + 1) * x - 1, 0,
567  (deskWidth + 1) * x - 1, height);
568  }
569 
570 }
571 
573 void UpdatePager(void)
574 {
575 
576  PagerType *pp;
577 
578  if(JUNLIKELY(shouldExit)) {
579  return;
580  }
581 
582  for(pp = pagers; pp; pp = pp->next) {
583 
584  /* Draw the pager. */
585  DrawPager(pp);
586 
587  /* Tell the tray to redraw. */
588  UpdateSpecificTray(pp->cp->tray, pp->cp);
589 
590  }
591 
592 }
593 
595 void SignalPager(const TimeType *now, int x, int y, Window w, void *data)
596 {
597  PagerType *pp = (PagerType*)data;
598  if(pp->cp->tray->window == w &&
599  abs(pp->mousex - x) < settings.doubleClickDelta &&
600  abs(pp->mousey - y) < settings.doubleClickDelta) {
601  if(GetTimeDifference(now, &pp->mouseTime) >= settings.popupDelay) {
602  const int desktop = GetPagerDesktop(pp, x - pp->cp->screenx,
603  y - pp->cp->screeny);
604  if(desktop >= 0 && desktop < settings.desktopCount) {
605  const char *desktopName = GetDesktopName(desktop);
606  if(desktopName) {
607  ShowPopup(x, y, desktopName, POPUP_PAGER);
608  }
609  }
610 
611  }
612  }
613 }
614 
616 void DrawPagerClient(const PagerType *pp, const ClientNode *np)
617 {
618 
619  int x, y;
620  int width, height;
621  int offx, offy;
622 
623  /* Don't draw the client if it isn't mapped. */
624  if(!(np->state.status & STAT_MAPPED)) {
625  return;
626  }
627  if(np->state.status & STAT_NOPAGER) {
628  return;
629  }
630 
631  /* Determine the desktop for the client. */
632  if(np->state.status & STAT_STICKY) {
635  } else {
636  offx = np->state.desktop % settings.desktopWidth;
637  offy = np->state.desktop / settings.desktopWidth;
638  }
639  offx *= pp->deskWidth + 1;
640  offy *= pp->deskHeight + 1;
641 
642  /* Determine the location and size of the client on the pager. */
643  x = 1 + ((np->x * pp->scalex) >> 16);
644  y = 1 + ((np->y * pp->scaley) >> 16);
645  width = (np->width * pp->scalex) >> 16;
646  height = (np->height * pp->scaley) >> 16;
647 
648  /* Normalize the size and offset. */
649  if(x + width > pp->deskWidth) {
650  width = pp->deskWidth - x;
651  }
652  if(y + height > pp->deskHeight) {
653  height = pp->deskHeight - y;
654  }
655  if(x < 0) {
656  width += x;
657  x = 0;
658  }
659  if(y < 0) {
660  height += y;
661  y = 0;
662  }
663 
664  /* Return if there's nothing to do. */
665  if(width <= 0 || height <= 0) {
666  return;
667  }
668 
669  /* Move to the correct desktop on the pager. */
670  x += offx;
671  y += offy;
672 
673  /* Draw the client outline. */
675  JXDrawRectangle(display, pp->cp->pixmap, rootGC, x, y, width, height);
676 
677  /* Fill the client if there's room. */
678  if(width > 1 && height > 1) {
679  ColorType fillColor;
680  if((np->state.status & STAT_ACTIVE)
681  && (np->state.desktop == currentDesktop
682  || (np->state.status & STAT_STICKY))) {
683  fillColor = COLOR_PAGER_ACTIVE_FG;
684  } else if(np->state.status & STAT_FLASH) {
685  fillColor = COLOR_PAGER_ACTIVE_FG;
686  } else {
687  fillColor = COLOR_PAGER_FG;
688  }
689  JXSetForeground(display, rootGC, colors[fillColor]);
690  JXFillRectangle(display, pp->cp->pixmap, rootGC, x + 1, y + 1,
691  width - 1, height - 1);
692  }
693 
694 }
695 

joewing.net / Projects / JWM