The Pedigree Project  0.1
winman.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #define __STDCPP_WANT_MATH_SPEC_FUNCS__ 0
21 
22 #define _USE_MATH_DEFINES
23 #include <arpa/inet.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <math.h>
27 #include <netinet/in.h>
28 #include <sched.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/klog.h>
34 #include <sys/mman.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <sys/un.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 
41 #include <cassert>
42 
43 #include <list>
44 #include <map>
45 #include <queue>
46 #include <set>
47 
48 #include "pedigree/native/graphics/Graphics.h"
49 #include "pedigree/native/input/Input.h"
50 
51 #include <cairo/cairo-ft.h>
52 #include <cairo/cairo.h>
53 
54 #include <pango/pangocairo.h>
55 
56 #include <pedigree_fb.h>
57 #include <protocol.h>
58 
59 #ifdef TARGET_LINUX
60 #include <SDL/SDL.h>
61 #endif
62 
63 #include "Png.h"
64 #include "util.h"
65 #include "winman.h"
66 
67 #define DEBUG_REDRAWS 0
68 
69 RootContainer *g_pRootContainer = 0;
70 
71 Window *g_pFocusWindow = 0;
72 
73 uint8_t *g_pBackbuffer = 0;
74 
75 std::map<uint64_t, Window *> *g_Windows;
76 
77 std::set<WObject *> g_PendingWindows;
78 
80 int g_iSocket = 0;
81 
83 int g_iControlPipe[2] = {0};
84 
85 std::string g_StatusField = "";
86 
87 std::queue<Input::InputNotification> g_InputQueue;
88 
89 void queueInputCallback(Input::InputNotification &note);
90 
91 ssize_t g_nWidth = 0;
92 ssize_t g_nHeight = 0;
93 
94 ssize_t g_CursorX = 0, g_LastCursorX = 0;
95 ssize_t g_CursorY = 0, g_LastCursorY = 0;
96 bool g_bCursorUpdate = false;
97 
98 bool g_bAlive = true;
99 
100 #define ALT_KEY (1ULL << 60)
101 #define SHIFT_KEY (1ULL << 61)
102 #define CTRL_KEY (1ULL << 62)
103 #define SPECIAL_KEY (1ULL << 63)
104 
106 #ifdef TARGET_LINUX
107 #define CLIENT_DEFAULT "./tui"
108 #else
109 #define CLIENT_DEFAULT "/applications/TUI"
110 #endif
111 
112 #define TEXTONLY_DEFAULT "/applications/ttyterm"
113 
114 #ifdef TARGET_LINUX
115 #define DEJAVU_FONT "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"
116 #else
117 #define DEJAVU_FONT "/system/fonts/DejaVuSansMono.ttf"
118 #endif
119 
120 #ifdef TARGET_LINUX
121 // Under Linux, where we want to run GDB on the window manager, we need to
122 // make sure the wrapper which, in Pedigree, resets our graphics mode, is not
123 // run. This helps track and debug the correct process.
124 #undef WINMAN_FORK_WRAPPER
125 #else
126 #define WINMAN_FORK_WRAPPER 1
127 #endif
128 
129 void startClient()
130 {
131  pid_t pid = fork();
132  if (pid == -1)
133  {
134  klog(LOG_CRIT, "winman: fork failed: %s\n", strerror(errno));
135  }
136  else if (pid == 0)
137  {
138  // Forked. Close all of our existing handles, we don't really want the
139  // client to inherit them.
141  close(g_iControlPipe[0]);
142  close(g_iControlPipe[1]);
143  close(g_iSocket);
144 
145  execl(CLIENT_DEFAULT, CLIENT_DEFAULT, NULL);
146  exit(1);
147  }
148 }
149 
150 void fps()
151 {
152  static unsigned int frames = 0;
153  static unsigned int start_time = 0;
154 
155  struct timeval now;
156  gettimeofday(&now, NULL);
157  frames++;
158  if (!start_time)
159  {
160  start_time = now.tv_sec;
161  }
162  else if (now.tv_sec - start_time >= 5)
163  {
164  float seconds = now.tv_sec - start_time;
165  float fps = frames / seconds;
166  klog(
167  LOG_INFO, "%d frames in %3.1f seconds = %6.3f FPS", frames, seconds,
168  fps);
169  start_time = now.tv_sec;
170  frames = 0;
171  }
172 }
173 
174 void handleDestroy(Window *pWindow)
175 {
176  Container *myParent = pWindow->getParent();
177  Window *newFocus = 0;
178  WObject *sibling = 0;
179 
180  // Find any siblings if we can.
181  WObject *siblingObject = myParent->getLeftSibling(pWindow);
182  if (!siblingObject)
183  {
184  siblingObject = myParent->getRightSibling(pWindow);
185  }
186 
187  // Will there be any children left?
188  if (myParent->getChildCount() > 1)
189  {
190  // Yes. Focus adjustment is mostly trivial.
191  }
192  else
193  {
194  // No. Focus adjustment is less trivial.
195  siblingObject = myParent->getLeft(pWindow);
196  if (!siblingObject)
197  {
198  siblingObject = myParent->getRight(pWindow);
199  }
200  if (!siblingObject)
201  {
202  siblingObject = myParent->getUp(pWindow);
203  }
204  if (!siblingObject)
205  {
206  siblingObject = myParent->getDown(pWindow);
207  }
208  }
209 
210  // Remove from the parent container.
211  myParent->removeChild(pWindow);
212  if (myParent->getChildCount() > 0)
213  {
214  myParent->retile();
215  }
216  else if (myParent->getParent())
217  {
218  Container *pContainer = static_cast<Container *>(myParent->getParent());
219  pContainer->removeChild(myParent);
220  pContainer->retile();
221  myParent = pContainer;
222  }
223 
224  // Assign new focus.
225  if (siblingObject)
226  {
227  while (siblingObject->getType() == WObject::Container)
228  {
229  Container *pContainer = static_cast<Container *>(siblingObject);
230  siblingObject = pContainer->getFocusWindow();
231  }
232 
233  if (siblingObject->getType() == WObject::Window)
234  {
235  newFocus = static_cast<Window *>(siblingObject);
236  }
237  }
238 
239  // All children are now pending a redraw.
240  g_PendingWindows.insert(myParent);
241 
242  // Switch focus, if needed.
243  if (g_pFocusWindow == pWindow)
244  {
245  g_pFocusWindow = newFocus;
246  if (newFocus)
247  {
248  newFocus->focus();
249  g_PendingWindows.insert(newFocus);
250  }
251  }
252 
253  // If we couldn't find a new focus window, we're devoid of windows.
254  if (!newFocus)
255  {
256  // assertion may be taking place before we remove the final window
257  // from g_Windows - hence the <= 1.
258  klog(LOG_INFO, "winman: no new focus window, terminating");
259  assert(g_Windows->size() <= 1);
260  g_bAlive = false;
261  }
262 }
263 
264 void handleMessage(char *messageData, struct sockaddr *src, socklen_t slen)
265 {
266  bool bResult = false;
268  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(messageData);
269 
270  size_t totalSize = sizeof(LibUiProtocol::WindowManagerMessage);
271 
272  if (pWinMan->messageCode == LibUiProtocol::Create)
273  {
275  reinterpret_cast<LibUiProtocol::CreateMessage *>(
276  messageData + sizeof(LibUiProtocol::WindowManagerMessage));
277  char *responseData = new char[totalSize];
278  memset(responseData, 0, totalSize);
279 
280  Container *pParent = g_pRootContainer;
281  if (g_pFocusWindow)
282  {
283  pParent = g_pFocusWindow->getParent();
284  }
285 
287 
288  struct sockaddr_un *sun = new struct sockaddr_un;
289  *sun = *((struct sockaddr_un *) src);
290 
291  std::string newTitle(pCreate->title);
292  Window *pWindow = new Window(
293  pWinMan->widgetHandle, g_iSocket, (struct sockaddr *) sun, slen,
294  pParent);
295  pWindow->setTitle(newTitle);
296 
297  g_PendingWindows.insert(pWindow);
298 
299  if (g_pFocusWindow)
300  {
301  g_pFocusWindow->nofocus();
302  }
303  g_pFocusWindow = pWindow;
304  pWindow->focus();
305 
307  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(
308  responseData);
309  pHeader->messageCode = LibUiProtocol::Create;
310  pHeader->widgetHandle = pWinMan->widgetHandle;
312  pHeader->isResponse = true;
313 
314  g_Windows->insert(std::make_pair(pWinMan->widgetHandle, pWindow));
315 
316  pWindow->sendMessage(responseData, totalSize);
317 
318  delete[] responseData;
319  }
320  else if (pWinMan->messageCode == LibUiProtocol::Sync)
321  {
322  std::map<uint64_t, Window *>::iterator it =
323  g_Windows->find(pWinMan->widgetHandle);
324  if (it != g_Windows->end())
325  {
326  Window *pWindow = it->second;
327  char *responseData = new char[totalSize];
328  memset(responseData, 0, totalSize);
329 
331  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(
332  responseData);
333  pHeader->messageCode = LibUiProtocol::Sync;
334  pHeader->widgetHandle = pWinMan->widgetHandle;
336  pHeader->isResponse = true;
337 
338  pWindow->sendMessage(responseData, totalSize);
339 
340  delete[] responseData;
341  }
342  }
343  else if (pWinMan->messageCode == LibUiProtocol::RequestRedraw)
344  {
346  reinterpret_cast<LibUiProtocol::RequestRedrawMessage *>(
347  messageData + sizeof(LibUiProtocol::WindowManagerMessage));
349  pRedrawMsg->x, pRedrawMsg->y, pRedrawMsg->width,
350  pRedrawMsg->height);
351 
352  std::map<uint64_t, Window *>::iterator it =
353  g_Windows->find(pWinMan->widgetHandle);
354  if (it != g_Windows->end())
355  {
356  Window *pWindow = it->second;
357  pWindow->setDirty(dirty);
358  g_PendingWindows.insert(pWindow);
359  }
360  }
361  else if (pWinMan->messageCode == LibUiProtocol::Destroy)
362  {
363  std::map<uint64_t, Window *>::iterator it =
364  g_Windows->find(pWinMan->widgetHandle);
365  if (it != g_Windows->end())
366  {
367  Window *pWindow = it->second;
368  // Remove from the tracked windows before handling the destroy.
369  g_Windows->erase(it);
370  handleDestroy(pWindow);
371 
372  char *responseData = new char[totalSize];
373  memset(responseData, 0, totalSize);
375  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(
376  responseData);
377  pHeader->messageCode = LibUiProtocol::Destroy;
378  pHeader->widgetHandle = pWinMan->widgetHandle;
379  pHeader->messageSize = 0;
380  pHeader->isResponse = true;
381 
382  pWindow->sendMessage(responseData, totalSize);
383 
384  delete[] responseData;
385 
386  delete pWindow;
387  }
388  }
389  else if (pWinMan->messageCode == LibUiProtocol::Nothing)
390  {
391  // We inject this to wake up checkForMessages and move on to rendering.
392  }
393  else if (pWinMan->messageCode == LibUiProtocol::SetTitle)
394  {
395  LibUiProtocol::SetTitleMessage *pTitleMsg =
396  reinterpret_cast<LibUiProtocol::SetTitleMessage *>(
397  messageData + sizeof(LibUiProtocol::WindowManagerMessage));
398 
399  std::map<uint64_t, Window *>::iterator it =
400  g_Windows->find(pWinMan->widgetHandle);
401  if (it != g_Windows->end())
402  {
403  Window *pWindow = it->second;
404  pWindow->setTitle(std::string(pTitleMsg->newTitle));
405  g_PendingWindows.insert(pWindow);
406  }
407  }
408  else
409  {
410  klog(LOG_INFO, "winman: unhandled message type");
411  }
412 }
413 
414 void checkForMessages()
415 {
416 #ifdef TARGET_LINUX
417  bool bTerminate = false;
418 
419  SDL_Event event;
420  int result = SDL_PollEvent(&event);
421  if (result)
422  {
423  switch (event.type)
424  {
425  case SDL_KEYDOWN:
426  if (event.key.keysym.sym == SDLK_ESCAPE)
427  bTerminate = true;
428  else
429  {
431  memset(&note, 0, sizeof(note));
432  note.type = Input::Key;
433  if (event.key.keysym.mod & KMOD_SHIFT)
434  note.data.key.key |= SHIFT_KEY;
435  if (event.key.keysym.mod & KMOD_ALT)
436  note.data.key.key |= ALT_KEY;
437  if (event.key.keysym.mod & KMOD_CTRL)
438  note.data.key.key |= CTRL_KEY;
439 
440  bool bSpecial = false;
441  if (event.key.keysym.sym == SDLK_LEFT)
442  {
443  memcpy(&note.data.key.key, "left", 4);
444  bSpecial = true;
445  }
446  else if (event.key.keysym.sym == SDLK_RIGHT)
447  {
448  memcpy(&note.data.key.key, "righ", 4);
449  bSpecial = true;
450  }
451  else if (event.key.keysym.sym == SDLK_UP)
452  {
453  memcpy(&note.data.key.key, "up", 2);
454  bSpecial = true;
455  }
456  else if (event.key.keysym.sym == SDLK_DOWN)
457  {
458  memcpy(&note.data.key.key, "down", 4);
459  bSpecial = true;
460  }
461 
462  if (bSpecial)
463  note.data.key.key |= SPECIAL_KEY;
464  else
465  note.data.key.key |= event.key.keysym.sym;
466 
467  // Make sure we're getting a modified key, not the actual
468  // modifier itself.
469  if (event.key.keysym.sym != SDLK_LALT &&
470  event.key.keysym.sym != SDLK_RALT &&
471  event.key.keysym.sym != SDLK_LSHIFT &&
472  event.key.keysym.sym != SDLK_RSHIFT &&
473  event.key.keysym.sym != SDLK_LCTRL &&
474  event.key.keysym.sym != SDLK_RCTRL)
475  {
476  if (note.data.key.key & SHIFT_KEY)
477  {
478  char c = note.data.key.key & 0xFF;
479  note.data.key.key &= ~0xFFFFFFFFUl;
480  if (isalpha(c))
481  {
482  note.data.key.key |= toupper(c);
483  }
484  else if (isdigit(c))
485  {
486  note.data.key.key = c - 0x10;
487  }
488  }
489  queueInputCallback(note);
490  }
491  }
492  break;
493 
494  case SDL_QUIT:
495  bTerminate = true;
496  break;
497  }
498  }
499 
500  if (bTerminate)
501  {
502  SDL_Quit();
503  exit(0);
504  }
505 
506  SDL_Delay(1);
507 #endif
508 
509  fd_set fds;
510  FD_ZERO(&fds);
511 
512  // Prepare the set for select()ing on.
513  FD_SET(g_iSocket, &fds);
514  FD_SET(g_iControlPipe[0], &fds);
515  int nMax = std::max(g_iSocket, g_iControlPipe[0]);
516 
517  struct timeval tv = {0, 0}; // Poll quickly.
518 
519  struct timeval *timeout = 0;
520 #ifdef TARGET_LINUX
521  timeout = &tv;
522 #endif
523 
524  // Do the deed - no timeout.
525  int ret = select(nMax + 1, &fds, 0, 0, timeout);
526  if (ret > 0)
527  {
528  if (FD_ISSET(g_iSocket, &fds))
529  {
530  // Socket has a datagram ready to handle.
531  // We use recvfrom so we can create a socket back to the client
532  // easily.
533  char msg[4096];
534  struct sockaddr_un saddr;
535  socklen_t slen = sizeof(saddr);
536  ssize_t sz = recvfrom(
537  g_iSocket, msg, 4096, 0, (struct sockaddr *) &saddr, &slen);
538  if (sz > 0)
539  {
540  // Handle!
541  handleMessage(msg, (struct sockaddr *) &saddr, slen);
542  }
543  }
544 
545  if (FD_ISSET(g_iControlPipe[0], &fds))
546  {
547  // Read a single byte (which will mean multiple writes to the pipe
548  // will wake select() up multiple times - good for the input queue).
549  char buf[2];
550  ssize_t bytes = read(g_iControlPipe[0], buf, 1);
551 
552  // Handle any pending input in the input queue.
553  if ((bytes > 0) && (!g_InputQueue.empty()))
554  {
555  Input::InputNotification n = g_InputQueue.front();
556  g_InputQueue.pop();
557 
558  queueInputCallback(n);
559  }
560  }
561  }
562 }
563 
564 enum ActualKey
565 {
566  None,
567  Left,
568  Right,
569  Up,
570  Down,
571 };
572 
580 void queueInputCallback(Input::InputNotification &note)
581 {
582  klog(LOG_INFO, "winman: system input (type=%d)", note.type);
583  static bool bResize = false;
584 
585  bool bHandled = false;
586  if (note.type & Input::Key)
587  {
588  uint64_t c = note.data.key.key;
589 
590  ActualKey realKey = None;
591 
592  if (c & SPECIAL_KEY)
593  {
594  uint32_t k = c & 0xFFFFFFFFULL;
595  char str[5];
596  memcpy(str, reinterpret_cast<char *>(&k), 4);
597  str[4] = 0;
598 
599  if (!strcmp(str, "left"))
600  {
601  realKey = Left;
602  }
603  else if (!strcmp(str, "righ"))
604  {
605  realKey = Right;
606  }
607  else if (!strcmp(str, "up"))
608  {
609  realKey = Up;
610  }
611  else if (!strcmp(str, "down"))
612  {
613  realKey = Down;
614  }
615  }
616 
618  if ((c & ALT_KEY) && (g_pFocusWindow != 0))
619  {
620  bool bShift = (c & SHIFT_KEY);
621 
622  klog(
623  LOG_INFO, "ALT-%d [%x%x] %c", (uint32_t) c,
624  (uint32_t)(c >> 32ULL), (uint32_t) c, (char) c);
625 
626  c &= 0xFFFFFFFFULL;
627  Container *focusParent = g_pFocusWindow->getParent();
628  Window *newFocus = 0;
629  WObject *sibling = 0;
630  if (bShift)
631  {
632  if (c == 'R')
633  {
634  klog(LOG_INFO, "winman: retiling");
635  g_pRootContainer->retile();
636  bHandled = true;
637  }
638  else if (c == 'Q')
639  {
640  // Are we the only child of the root container?
641  bool bSafe = true;
642  if (focusParent == g_pRootContainer)
643  {
644  if (focusParent->getChildCount() == 1)
645  {
646  klog(
647  LOG_INFO, "winman: can't (yet) terminate only "
648  "child of root container!");
649  bSafe = false;
650  }
651  }
652 
653  if (bSafe)
654  {
655  size_t totalSize =
657  char *buffer = new char[totalSize];
658  memset(buffer, 0, totalSize);
659 
661  reinterpret_cast<
663  pHeader->widgetHandle = g_pFocusWindow->getHandle();
664  pHeader->messageSize = 0;
665  pHeader->messageCode = LibUiProtocol::Destroy;
666  pHeader->isResponse = false;
667 
668  g_pFocusWindow->sendMessage(buffer, totalSize);
669 
670  delete[] buffer;
671 
672  bHandled = true;
673  }
674  }
675  }
676  else if (c == '\n' || c == '\r')
677  {
678  // Add window to active container.
679  startClient();
680  bHandled = true;
681  }
682  else if ((c == 'v') || (c == 'h'))
683  {
684  ::Container::Layout layout;
685  if (c == 'v')
686  {
687  layout = Container::Stacked;
688  }
689  else if (c == 'h')
690  {
691  layout = Container::SideBySide;
692  }
693  else
694  {
695  layout = Container::SideBySide;
696  }
697 
698  // Replace focus window with container (Container::replace) in
699  // the relevant layout mode.
700  if (focusParent->getChildCount() == 1)
701  {
702  focusParent->setLayout(layout);
703  }
704  else if (focusParent->getLayout() != layout)
705  {
706  Container *pNewContainer = new Container(focusParent);
707  pNewContainer->addChild(g_pFocusWindow, true);
708  g_pFocusWindow->setParent(pNewContainer);
709 
710  focusParent->replaceChild(g_pFocusWindow, pNewContainer);
711  focusParent->retile();
712  pNewContainer->setLayout(layout);
713  }
714 
715  bHandled = true;
716  }
717  else if (c == 'r')
718  {
719  // State machine: enter 'resize mode', which allows us to resize
720  // the container in which the window with focus is in.
721  bResize = true;
722  bHandled = true;
723  g_StatusField = "<resize mode>";
724 
725  // Don't transmit resizes to the client(s) yet.
726  g_pRootContainer->norefresh();
727  }
728  else if (realKey != None)
729  {
730  if (realKey == Left)
731  {
732  sibling = focusParent->getLeft(g_pFocusWindow);
733  }
734  else if (realKey == Up)
735  {
736  sibling = focusParent->getUp(g_pFocusWindow);
737  }
738  else if (realKey == Down)
739  {
740  sibling = focusParent->getDown(g_pFocusWindow);
741  }
742  else if (realKey == Right)
743  {
744  sibling = focusParent->getRight(g_pFocusWindow);
745  }
746 
747  // Not edge of screen?
748  if (sibling)
749  {
750  while (sibling->getType() == WObject::Container)
751  {
752  Container *pContainer =
753  static_cast<Container *>(sibling);
754  sibling = pContainer->getFocusWindow();
755  }
756 
757  if (sibling->getType() == WObject::Window)
758  {
759  newFocus = static_cast<Window *>(sibling);
760  }
761  }
762 
763  bHandled = true;
764  }
765 
766  if (newFocus)
767  {
768  if (g_pFocusWindow)
769  {
770  g_PendingWindows.insert(g_pFocusWindow);
771  g_pFocusWindow->nofocus();
772  }
773 
774  g_PendingWindows.insert(newFocus);
775 
776  g_pFocusWindow = newFocus;
777  g_pFocusWindow->focus();
778  }
779  }
780 
781  if ((!bHandled) && bResize && (g_pFocusWindow))
782  {
783  bool bWakeup = true;
784  bHandled = true;
785 
786  Container *focusParent = g_pFocusWindow->getParent();
787  WObject *sibling = 0;
788  if (c == '\e')
789  {
790  g_StatusField = " ";
791  bResize = false;
792 
793  // Okay, now the client(s) can get a refreshed context.
794  g_pRootContainer->yesrefresh();
795  }
796  else if (realKey == Left)
797  {
798  sibling = focusParent->getLeft(g_pFocusWindow);
799  if (sibling)
800  {
801  sibling->resize(-10, 0);
802  }
803  }
804  else if (realKey == Right)
805  {
806  sibling = focusParent->getRight(g_pFocusWindow);
807  if (sibling)
808  {
809  g_pFocusWindow->resize(10, 0);
810  }
811  }
812  else if (realKey == Up)
813  {
814  sibling = focusParent->getUp(g_pFocusWindow);
815  if (sibling)
816  {
817  sibling->resize(0, -10);
818  }
819  }
820  else if (realKey == Down)
821  {
822  sibling = focusParent->getDown(g_pFocusWindow);
823  if (sibling)
824  {
825  g_pFocusWindow->resize(0, 10);
826  }
827  }
828  else
829  {
830  bWakeup = false;
831  }
832 
833  if (bWakeup)
834  {
835  g_PendingWindows.insert(sibling);
836  g_PendingWindows.insert(g_pFocusWindow);
837  }
838  }
839  }
840 
841  if (note.type & Input::Mouse)
842  {
843  // Update our cursor position.
846  g_CursorX += note.data.pointy.relx;
847  g_CursorY -= note.data.pointy.rely;
848 
849  // Constrain.
850  if (g_CursorX < 0)
851  g_CursorX = 0;
852  if (g_CursorY < 0)
853  g_CursorY = 0;
854  if (g_CursorX >= g_nWidth)
855  g_CursorX = g_nWidth - 1;
856  if (g_CursorY >= g_nHeight)
857  g_CursorY = g_nHeight - 1;
858 
859  klog(
860  LOG_INFO, "Cursor update %zd, %zd [rel %zd %zd]", g_CursorX,
861  g_CursorY, note.data.pointy.relx, note.data.pointy.rely);
862 
863  // Trigger a render.
864  g_bCursorUpdate = true;
865  }
866 
867  if ((!bHandled) && (g_pFocusWindow))
868  {
869  // Forward event to focus window.
870  size_t totalSize = sizeof(LibUiProtocol::WindowManagerMessage);
871  if (note.type & Input::Key)
872  totalSize += sizeof(LibUiProtocol::KeyEventMessage);
873  else if (note.type & Input::RawKey)
874  totalSize += sizeof(LibUiProtocol::RawKeyEventMessage);
875 
876  char *buffer = new char[totalSize];
877  memset(buffer, 0, totalSize);
878 
880  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(buffer);
881  pHeader->widgetHandle = g_pFocusWindow->getHandle();
882  pHeader->messageSize = totalSize - sizeof(*pHeader);
883  pHeader->isResponse = false;
884 
885  if (note.type & Input::Key)
886  {
887  pHeader->messageCode = LibUiProtocol::KeyEvent;
888 
889  LibUiProtocol::KeyEventMessage *pKeyEvent =
890  reinterpret_cast<LibUiProtocol::KeyEventMessage *>(
891  buffer + sizeof(LibUiProtocol::WindowManagerMessage));
892  pKeyEvent->state = LibUiProtocol::Up;
893  pKeyEvent->key = note.data.key.key;
894 
895  g_pFocusWindow->sendMessage(buffer, totalSize);
896  }
897  else if (note.type & Input::RawKey)
898  {
899  pHeader->messageCode = LibUiProtocol::RawKeyEvent;
900 
902  reinterpret_cast<LibUiProtocol::RawKeyEventMessage *>(
903  buffer + sizeof(LibUiProtocol::WindowManagerMessage));
904  pKeyEvent->state = note.data.rawkey.keyUp ? LibUiProtocol::Up :
905  LibUiProtocol::Down;
906  pKeyEvent->scancode = note.data.rawkey.scancode;
907 
908  g_pFocusWindow->sendMessage(buffer, totalSize);
909  }
910 
911  delete[] buffer;
912  }
913 }
914 
916 void systemInputCallback(Input::InputNotification &note)
917 {
918  Input::InputNotification n = note;
919  g_InputQueue.push(n);
920 
921  // Wake up any pending select() - input pushed to queue.
922  ssize_t r = 0;
923  while (r <= 0)
924  {
925  r = write(g_iControlPipe[1], "w", 1);
926  }
927 }
928 
929 void sigchld(int s)
930 {
931  klog(LOG_INFO, "SIGCHLD");
932  int status = 0;
933  pid_t pid = waitpid(-1, &status, WNOHANG);
934  if (pid <= 0)
935  {
936  klog(LOG_ALERT, "SIGCHLD handler called but no children to reap.");
937  return;
938  }
939 
940  if (WIFEXITED(status))
941  {
942  int exit_status = WEXITSTATUS(status);
943  klog(LOG_INFO, "Child %d exited with status %d.", pid, exit_status);
944  }
945  else if (WIFSIGNALED(status))
946  {
947  int term_signal = WTERMSIG(status);
948  klog(LOG_INFO, "Child %d terminated with signal %d.", pid, term_signal);
949  }
950  else
951  {
952  klog(LOG_INFO, "Child %d terminated for an unknown reason.", pid);
953  }
954 
955  // Now, we don't know what resources it held.
956  for (std::map<uint64_t, Window *>::iterator it = g_Windows->begin();
957  it != g_Windows->end();)
958  {
959  uint64_t handle = it->first;
960  pid_t window_pid = (handle >> 32ULL) & 0xFFFFFFFFU;
961  klog(LOG_INFO, "%d vs %d", window_pid, pid);
962  if (window_pid == pid)
963  {
964  klog(LOG_INFO, "Found a child window for the terminated child.");
965  handleDestroy(it->second);
966  delete it->second;
967 
968  g_Windows->erase(it++);
969  }
970  else
971  {
972  ++it;
973  }
974  }
975 }
976 
977 void infoPanel(cairo_t *cr)
978 {
979  // Create a nice little bar at the bottom of the screen.
980  cairo_save(cr);
981  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
982 
983  cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.8);
984  cairo_rectangle(cr, 0, g_nHeight - 24, g_nWidth, 24);
985  cairo_fill(cr);
986 
987  PangoLayout *layout = pango_cairo_create_layout(cr);
988  PangoFontDescription *desc =
989  pango_font_description_from_string(WINMAN_PANGO_FONT);
990 
991  pango_layout_set_markup(layout, "The Pedigree Operating System", -1);
992 
993  pango_layout_set_font_description(layout, desc);
994  pango_font_description_free(desc);
995 
996  cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
997  cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
998  cairo_move_to(cr, 3, (g_nHeight - 24) + 3);
999  pango_cairo_show_layout(cr, layout);
1000 
1001  if (g_StatusField.length())
1002  {
1003  gchar *safe_markup = g_markup_escape_text(g_StatusField.c_str(), -1);
1004  pango_layout_set_markup(layout, safe_markup, -1);
1005  int width = 0, height = 0;
1006  pango_layout_get_size(layout, &width, &height);
1007 
1008  cairo_move_to(cr, g_nWidth - 3 - width, (g_nHeight - 24) + 3);
1009  pango_cairo_show_layout(cr, layout);
1010  g_StatusField.clear();
1011  g_free(safe_markup);
1012  }
1013 
1014  g_object_unref(layout);
1015  cairo_restore(cr);
1016 }
1017 
1018 int main(int argc, char *argv[])
1019 {
1020 #ifdef TARGET_LINUX
1021  openlog("winman", LOG_PID, LOG_USER);
1022 #endif
1023 
1024  klog(LOG_INFO, "winman: starting up...");
1025  fprintf(stderr, "I am PID %d\n", getpid());
1026 
1027  // Create ourselves a lock file so we don't end up getting run twice.
1029  int fd = open("runtimeĀ»/winman.lck", O_WRONLY | O_EXCL | O_CREAT, 0500);
1030  if (fd < 0)
1031  {
1032  fprintf(stderr, "winman: lock file exists, terminating.\n");
1033  return EXIT_FAILURE;
1034  }
1035  close(fd);
1036 
1037  Framebuffer *pFramebuffer = new Framebuffer();
1038  if (!pFramebuffer->initialise())
1039  {
1040  fprintf(stderr, "winman: framebuffer initialisation failed\n");
1041  return EXIT_FAILURE;
1042  }
1043 
1044  // Save current mode so we can restore it on quit.
1045  pFramebuffer->storeMode();
1046 
1047  // Kick off a 'window manager' process group, fork to run the modeset shim.
1048  setpgid(0, 0);
1049 #ifdef WINMAN_FORK_WRAPPER
1050  pid_t child = fork();
1051  if (child == -1)
1052  {
1053  fprintf(stderr, "winman: could not fork: %s\n", strerror(errno));
1054  return 1;
1055  }
1056  else if (child != 0)
1057  {
1058  // Wait for the child (ie, real window manager process) to terminate.
1059  int status = 0;
1060  waitpid(child, &status, 0);
1061 
1062  // Restore old graphics mode.
1063  pFramebuffer->restoreMode();
1064  delete pFramebuffer;
1065 
1066  // Termination information
1067  if (WIFEXITED(status))
1068  {
1069  fprintf(
1070  stderr, "winman: terminated with status %d\n",
1071  WEXITSTATUS(status));
1072  }
1073  else if (WIFSIGNALED(status))
1074  {
1075  fprintf(
1076  stderr, "winman: terminated by signal %d\n", WTERMSIG(status));
1077  }
1078  else
1079  {
1080  fprintf(stderr, "winman: terminated by unknown means\n");
1081  }
1082 
1083  // Terminate our process group.
1084  kill(0, SIGTERM);
1085  return 0;
1086  }
1087 #endif
1088 
1089  // Create control pipe.
1090  int result = pipe(g_iControlPipe);
1091  if (result != 0)
1092  {
1093  fprintf(
1094  stderr, "winman: couldn't create control pipe [%s]\n",
1095  strerror(errno));
1096  return EXIT_FAILURE;
1097  }
1098 
1099  // Create listening socket.
1100  g_iSocket = socket(AF_UNIX, SOCK_DGRAM, 0);
1101  if (g_iSocket < 0)
1102  {
1103  fprintf(
1104  stderr,
1105  "error: couldn't create the pedigree-winman IPC endpoint! [%s]",
1106  strerror(errno));
1107  return EXIT_FAILURE;
1108  }
1109 
1110  // Bind.
1111  struct sockaddr_un bind_addr;
1112  bind_addr.sun_family = AF_UNIX;
1113  memset(bind_addr.sun_path, 0, sizeof bind_addr.sun_path);
1114  strncpy(bind_addr.sun_path, WINMAN_SOCKET_PATH, sizeof bind_addr.sun_path);
1115  socklen_t socklen = sizeof(bind_addr);
1116  result = bind(g_iSocket, (struct sockaddr *) &bind_addr, socklen);
1117  if (result != 0)
1118  {
1119  fprintf(
1120  stderr, "winman: couldn't bind to %s [%s]\n", WINMAN_SOCKET_PATH,
1121  strerror(errno));
1122  return EXIT_FAILURE;
1123  }
1124 
1125 #ifdef TARGET_LINUX
1126  if (SDL_Init(SDL_INIT_VIDEO) != 0)
1127  {
1128  fprintf(stderr, "winman: SDL initialisation failed.\n");
1129  return EXIT_FAILURE;
1130  }
1131 #endif
1132 
1133  // Can we set the graphics mode we want?
1135  result = pFramebuffer->enterMode(1024, 768, 32);
1136  if (result != 0)
1137  return result;
1138 
1139  g_nWidth = pFramebuffer->getWidth();
1140  g_nHeight = pFramebuffer->getHeight();
1141 
1142  klog(LOG_INFO, "Actual mode is %ux%u", g_nWidth, g_nHeight);
1143 
1144  cairo_format_t format = pFramebuffer->getFormat();
1145 
1146  int stride = cairo_format_stride_for_width(format, g_nWidth);
1147 
1148  void *framebufferVirt = pFramebuffer->getFramebuffer();
1149 
1150  cairo_surface_t *surface = cairo_image_surface_create_for_data(
1151  (uint8_t *) framebufferVirt, format, g_nWidth, g_nHeight, stride);
1152  cairo_t *cr = cairo_create(surface);
1153 
1154  FT_Library font_library;
1155  FT_Face ft_face;
1156  int e = FT_Init_FreeType(&font_library);
1157  if (e)
1158  {
1159  klog(LOG_CRIT, "error: couldn't initialise Freetype");
1160  return 0;
1161  }
1162 
1163  e = FT_New_Face(font_library, DEJAVU_FONT, 0, &ft_face);
1164  if (e)
1165  {
1166  klog(LOG_CRIT, "winman: error: couldn't load required font");
1167  return 0;
1168  }
1169 
1170  cairo_user_data_key_t key;
1171  cairo_font_face_t *font_face;
1172 
1173  font_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0);
1174  cairo_font_face_set_user_data(
1175  font_face, &key, ft_face, (cairo_destroy_func_t) FT_Done_Face);
1176  cairo_set_font_face(cr, font_face);
1177 
1178  Png *wallpaper = 0;
1179  FILE *fp = fopen("/system/wallpaper/fields.png", "rb");
1180  if (fp)
1181  {
1182  fclose(fp);
1183 
1184  // A nice wallpaper.
1185  wallpaper = new Png("/system/wallpaper/trees.png");
1186  wallpaper->render(cr, 0, 0, g_nWidth, g_nHeight);
1187  }
1188  else
1189  {
1190  // No PNG found, fall back to a blue background.
1191  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1192  cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
1193  cairo_rectangle(cr, 0, 0, g_nWidth, g_nHeight);
1194  cairo_fill(cr);
1195  }
1196 
1197  g_pRootContainer = new RootContainer(g_nWidth, g_nHeight - 24);
1198 
1199  infoPanel(cr);
1200 
1201  klog(LOG_INFO, "winman: entering main loop pid=%d", getpid());
1202 
1203  g_Windows = new std::map<uint64_t, Window *>();
1204 
1205 // Install our global input callback before we kick off our client.
1206 #ifndef TARGET_LINUX
1207  Input::installCallback(
1208  Input::RawKey | Input::Key | Input::Mouse, systemInputCallback);
1209 #endif
1210 
1211  // Render all window decorations and non-client display elements first up.
1212  g_pRootContainer->render(cr);
1213 
1214  // Kick off the first render before any windows are open.
1215  cairo_surface_flush(surface);
1216  pFramebuffer->flush(0, 0, g_nWidth, g_nHeight);
1217 
1218  // Prepare for SIGCHLD messages from children.
1219  struct sigaction act;
1220  memset(&act, 0, sizeof(act));
1221  act.sa_handler = sigchld;
1222  sigemptyset(&act.sa_mask);
1223  act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1224  sigaction(SIGCHLD, &act, NULL);
1225 
1226  // Load first tile.
1227  startClient();
1228 
1229  // Main loop: logic & message handling goes here!
1230  DirtyRectangle renderDirty;
1231  g_bAlive = true;
1232  while (g_bAlive)
1233  {
1234  // Check for any messages coming in from windows, asynchronously.
1235  checkForMessages();
1236 
1237  // Check for any windows that may need rendering.
1238  if ((!g_PendingWindows.empty()) || g_StatusField.length() ||
1239  g_bCursorUpdate)
1240  {
1241  // Render each window, also ensuring the wallpaper is rendered again
1242  // so that windows with alpha look correct.
1243  std::set<WObject *>::iterator it = g_PendingWindows.begin();
1244  size_t nDirty = g_StatusField.length() ? 1 : 0;
1245  if (g_bCursorUpdate)
1246  ++nDirty;
1247  for (; it != g_PendingWindows.end(); ++it)
1248  {
1249  Window *pWindow = 0;
1250  if ((*it)->getType() == WObject::Window)
1251  {
1252  pWindow = static_cast<Window *>(*it);
1253  }
1254 
1255  if (pWindow && !pWindow->isDirty())
1256  {
1257  continue;
1258  }
1259  else
1260  {
1261  ++nDirty;
1262  }
1263 
1264  PedigreeGraphics::Rect rt = (*it)->getCopyDimensions();
1265  PedigreeGraphics::Rect dirty = rt;
1266  if (pWindow)
1267  {
1268  dirty = pWindow->getDirty();
1269  }
1270 
1271  // Render wallpaper.
1272  if (wallpaper)
1273  {
1274  wallpaper->renderPartial(
1275  cr, rt.getX() + dirty.getX(), rt.getY() + dirty.getY(),
1276  0, 0, dirty.getW(), dirty.getH(), g_nWidth, g_nHeight);
1277 #if DEBUG_REDRAWS
1278  cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
1279  cairo_rectangle(
1280  cr, rt.getX() + dirty.getX(), rt.getY() + dirty.getY(),
1281  dirty.getW(), dirty.getH());
1282  cairo_stroke(cr);
1283 #endif
1284  }
1285  else
1286  {
1287  // Boring background.
1288  cairo_set_source_rgba(cr, 0, 0, 1.0, 1.0);
1289  cairo_rectangle(
1290  cr, rt.getX() + dirty.getX(), rt.getY() + dirty.getY(),
1291  dirty.getW(), dirty.getH());
1292 #if DEBUG_REDRAWS
1293  cairo_stroke_preserve(cr);
1294 #endif
1295  cairo_fill(cr);
1296  }
1297 
1298  // Render window.
1299  (*it)->render(cr);
1300 
1301  // Update the dirty rectangle.
1302  renderDirty.point(
1303  rt.getX() + dirty.getX(), rt.getX() + dirty.getY());
1304  renderDirty.point(
1305  rt.getX() + dirty.getX() + dirty.getW(),
1306  rt.getX() + dirty.getY() + dirty.getH());
1307  }
1308 
1309  // Empty out the list in full.
1310  g_PendingWindows.clear();
1311 
1312  // Only do rendering if we actually did some rendering!
1313  if (!nDirty)
1314  {
1315  renderDirty.reset();
1316  continue;
1317  }
1318 
1319  if (g_StatusField.length())
1320  {
1321  infoPanel(cr);
1322  }
1323 
1324 #if 0
1325  // After all else is said and done, render the cursor.
1326  cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1327  cairo_rectangle(
1328  cr,
1329  g_CursorX,
1330  g_CursorY,
1331  32,
1332  32);
1333  cairo_fill(cr);
1334  renderDirty.point(g_CursorX, g_CursorY);
1335  renderDirty.point(g_CursorX + 32, g_CursorY + 32);
1336 
1337  // We'll want to redraw the area we just obstructed shortly.
1338  g_LastCursorX = g_CursorX;
1339  g_LastCursorY = g_CursorY;
1340 
1341  // Done drawing cursor.
1342  g_bCursorUpdate = false;
1343 #endif
1344 
1345  // Flush the cairo surface, which will ensure the most recent data
1346  // is in the framebuffer ready to send to the device.
1347  cairo_surface_flush(surface);
1348 
1349  // Submit a redraw to the graphics card.
1350  pFramebuffer->flush(
1351  renderDirty.getX(), renderDirty.getY(), renderDirty.getWidth(),
1352  renderDirty.getHeight());
1353 
1354  // Wipe out the dirty rectangle - we're all done.
1355  renderDirty.reset();
1356  }
1357  }
1358 
1360  klog(LOG_INFO, "winman terminating");
1361 
1362  // Clean up wallpaper, if one exists.
1363  if (wallpaper)
1364  {
1365  delete wallpaper;
1366  }
1367 
1368  delete g_pRootContainer;
1369 
1370  cairo_font_face_destroy(font_face);
1371 
1372  FT_Done_Face(ft_face);
1373  FT_Done_FreeType(font_library);
1374 
1375  cairo_surface_destroy(surface);
1376 
1377  // Clean up the framebuffer finally.
1378  delete pFramebuffer;
1379 
1380 #ifdef TARGET_LINUX
1381  SDL_Quit();
1382 #endif
1383 
1384  return 0;
1385 }
WObject * getParent() const
Definition: winman.h:530
void restoreMode()
Definition: fb.cc:85
void replaceChild(WObject *pChild, WObject *pNewChild)
Definition: winman.h:384
size_t getChildCount() const
Definition: winman.h:448
int enterMode(size_t desiredW, size_t desiredH, size_t desiredBpp)
Definition: fb.cc:98
WObject * getRightSibling(const WObject *pChild) const
Definition: objects.cc:588
virtual void yesrefresh()
Refresh context on every reposition.
Definition: objects.cc:789
Definition: winman.h:195
bool initialise()
Definition: fb.cc:56
char title[256]
Initial title for this widget.
Definition: protocol.h:200
WObject * getLeftSibling(const WObject *pChild) const
Definition: objects.cc:568
WObject * getDown(const WObject *obj) const
Definition: objects.cc:730
void addChild(WObject *pChild, bool bNoRetile=false)
Definition: winman.h:365
handle_t widgetHandle
Handle for the widget being referred to. Zero if no widget.
Definition: protocol.h:171
void retile()
Definition: objects.cc:414
void flush(size_t x, size_t y, size_t w, size_t h)
Definition: fb.cc:186
size_t messageSize
Size of the data in the message (after this header).
Definition: protocol.h:174
WObject * getRight(const WObject *obj) const
Definition: objects.cc:656
#define assert(x)
Definition: assert.h:37
void render(cairo_t *cr)
Definition: objects.cc:550
Abstracts the system&#39;s framebuffer offering.
void removeChild(WObject *pChild)
Definition: winman.h:426
virtual void * getFramebuffer()
WObject * getLeft(const WObject *obj) const
Definition: objects.cc:619
MessageIdentifiers messageCode
Code of the message being sent.
Definition: protocol.h:168
void storeMode()
Definition: fb.cc:70
virtual void norefresh()
Don&#39;t refresh the context on every reposition.
Definition: objects.cc:779
WObject * getUp(const WObject *obj) const
Definition: objects.cc:693
bool isResponse
Whether this message is a response from the window manager or not.
Definition: protocol.h:177