The Pedigree Project  0.1
Widget.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 #include <Widget.h>
21 
22 #include "pedigree/native/ipc/Ipc.h"
23 
25 #include <unistd.h>
26 
28 #include <arpa/inet.h>
29 #include <errno.h>
30 #include <netinet/in.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/select.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <syslog.h>
38 
39 #ifdef TARGET_LINUX
40 #include <bsd/string.h> // strlcpy
41 #endif
42 
43 #include <map>
44 #include <queue>
45 
46 #include "protocol.h"
47 
48 using namespace LibUiProtocol;
49 
50 #define UNIX_PATH_MAX (sizeof sockaddr_un::sun_path)
51 
52 #define PEDIGREE_WINMAN_ENDPOINT "pedigree-winman"
53 
55 static bool defaultEventHandler(
56  WidgetMessages message, size_t dataSize, const void *dataBuffer)
57 {
58  return false;
59 }
60 
61 std::map<uint64_t, Widget *> Widget::s_KnownWidgets;
62 std::queue<char *> Widget::s_PendingMessages;
64 
65 Widget::Widget()
66  : m_bConstructed(false), m_pFramebuffer(0), m_Handle(0),
67  m_EventCallback(defaultEventHandler), m_Socket(-1), m_SharedFramebuffer(0)
68 {
69  ++s_NumWidgets;
70 }
71 
72 Widget::~Widget()
73 {
74  destroy();
75 
76  --s_NumWidgets;
77 
78  // Wipe out any pending messages that might be left behind.
79  if (!s_NumWidgets)
80  {
81  while (!Widget::s_PendingMessages.empty())
82  {
83  char *p = Widget::s_PendingMessages.front();
84  delete[] p;
86  }
87  }
88 }
89 
91  const char *endpoint, const char *title, widgetCallback_t cb,
92  PedigreeGraphics::Rect &dimensions)
93 {
94  if (m_Handle)
95  {
96  // Already constructed!
97  return false;
98  }
99 
101  if (!cb)
102  return false;
103 
104  struct sockaddr_un meaddr;
105  memset(&meaddr, 0, sizeof(meaddr));
106  meaddr.sun_family = AF_UNIX;
107  snprintf(meaddr.sun_path, UNIX_PATH_MAX, CLIENT_SOCKET_BASE, endpoint);
108 
109  struct sockaddr_un saddr;
110  memset(&saddr, 0, sizeof(saddr));
111  saddr.sun_family = AF_UNIX;
112  strncpy(saddr.sun_path, WINMAN_SOCKET_PATH, UNIX_PATH_MAX);
113 
114  // Create socket for window manager communication.
115  m_Socket = socket(AF_UNIX, SOCK_DGRAM, 0);
116  if (m_Socket < 0)
117  return false;
118 
119  // Connect to window manager.
120  if (bind(m_Socket, (struct sockaddr *) &meaddr, sizeof(meaddr)) != 0)
121  {
122  syslog(LOG_ALERT, "widget bind failed: %s", strerror(errno));
123  close(m_Socket);
124  m_Socket = -1;
125  return false;
126  }
127  if (connect(m_Socket, (struct sockaddr *) &saddr, sizeof(saddr)) != 0)
128  {
129  syslog(LOG_ALERT, "widget connection failed: %s", strerror(errno));
130  close(m_Socket);
131  m_Socket = -1;
132  return false;
133  }
134 
135  // Construct the handle first.
136  uint64_t pid = getpid();
137  uintptr_t widgetPointer = reinterpret_cast<uintptr_t>(this);
138 #ifdef BITS_64
139  m_Handle =
140  (pid << 32) |
141  (((widgetPointer >> 32) | (widgetPointer & 0xFFFFFFFF)) & 0xFFFFFFFF);
142 #else
143  m_Handle = (pid << 32) | (widgetPointer);
144 #endif
145 
146  // Prepare a message to send.
147  size_t totalSize = sizeof(WindowManagerMessage) + sizeof(CreateMessage);
148  char *messageData = new char[totalSize];
149  memset(messageData, 0, totalSize);
150  WindowManagerMessage *pWinMan =
151  reinterpret_cast<WindowManagerMessage *>(messageData);
152  CreateMessage *pCreate = reinterpret_cast<CreateMessage *>(
153  messageData + sizeof(WindowManagerMessage));
154 
155  // Fill.
156  pWinMan->messageCode = Create;
157  pWinMan->messageSize = sizeof(CreateMessage);
158  pWinMan->widgetHandle = m_Handle;
159  pWinMan->isResponse = false;
160  strlcpy(pCreate->endpoint, endpoint, 256);
161  strlcpy(pCreate->title, title, 256);
162  pCreate->minWidth = dimensions.getW();
163  pCreate->minHeight = dimensions.getH();
164  pCreate->rigid = true;
165 
166  // Send the message off to the window manager and wait for a response. The
167  // response will contain a shared memory region we can use as our window
168  // framebuffer.
169  send(m_Socket, messageData, totalSize, 0);
170  delete[] messageData;
171 
172  s_KnownWidgets.insert(std::make_pair(m_Handle, this));
173 
174  // Wait for the ACK.
175  Widget::checkForMessage(Create, true);
176 
177  m_EventCallback = cb;
178 
179  // Wait for the initial Reposition message to come in, as it is needed to
180  // properly allocate a window buffer.
181  Widget::checkForMessage(LibUiProtocol::Reposition, false);
182 
183  // Handle any remaining messages before we consider ourselves constructed.
185 
186  return true;
187 }
188 
189 bool Widget::setTitle(const std::string &newTitle)
190 {
191  // Constructed yet?
192  if (!m_Handle)
193  return false;
194 
195  size_t titleLength = newTitle.length();
196  if (titleLength > 511)
197  titleLength = 511;
198 
199  // Allocate the message.
200  ssize_t totalSize = sizeof(WindowManagerMessage) + sizeof(SetTitleMessage);
201  char *messageData = new char[totalSize];
202  memset(messageData, 0, totalSize);
203  WindowManagerMessage *pWinMan =
204  reinterpret_cast<WindowManagerMessage *>(messageData);
205  SetTitleMessage *pMessage = reinterpret_cast<SetTitleMessage *>(
206  messageData + sizeof(WindowManagerMessage));
207 
208  // Fill the message.
209  pWinMan->messageCode = SetTitle;
210  pWinMan->messageSize = titleLength;
211  pWinMan->widgetHandle = m_Handle;
212  pWinMan->isResponse = false;
213 
214  newTitle.copy(pMessage->newTitle, sizeof pMessage->newTitle);
215 
216  // Transmit.
217  bool result = send(m_Socket, messageData, totalSize, 0) == totalSize;
218 
219  // Clean up.
220  delete[] messageData;
221 
222  return result;
223 }
224 
226 {
227  // Constructed yet?
228  if (!m_Handle)
229  return false;
230 
231  ssize_t totalSize =
232  sizeof(WindowManagerMessage) + sizeof(RequestRedrawMessage);
233  char *messageData = new char[totalSize];
234  memset(messageData, 0, totalSize);
235  WindowManagerMessage *pWinMan =
236  reinterpret_cast<WindowManagerMessage *>(messageData);
237  RequestRedrawMessage *pMessage = reinterpret_cast<RequestRedrawMessage *>(
238  messageData + sizeof(WindowManagerMessage));
239 
240  // Fill the message.
241  pWinMan->messageCode = RequestRedraw;
242  pWinMan->messageSize = sizeof(RequestRedrawMessage);
243  pWinMan->widgetHandle = m_Handle;
244  pWinMan->isResponse = false;
245  pMessage->x = rt.getX();
246  pMessage->y = rt.getY();
247  pMessage->width = rt.getW();
248  pMessage->height = rt.getH();
249 
250  // Transmit.
251  bool bRet = false;
252  bRet = send(m_Socket, messageData, totalSize, 0) == totalSize;
253  delete[] messageData;
254 
255  // Wait for an ACK message.
256  Widget::checkForMessage(LibUiProtocol::RequestRedraw, true);
257 
258  // Now that we have completed the redraw, handle any pending messages.
259  // For example, the reason we are waiting for the redraw could be because
260  // the window manager is handling keyboard events, and each of those events
261  // is causing a packet to be sent. If we don't handle that, a select() on
262  // our socket will not reflect the pending messages, and input will appear
263  // to get eaten.
264  handlePendingMessages();
265 
266  return bRet;
267 }
268 
269 bool Widget::visibility(bool vis)
270 {
271  // Constructed yet?
272  if (!m_Handle)
273  return false;
274 
275  // Allocate the message.
276  ssize_t totalSize =
277  sizeof(WindowManagerMessage) + sizeof(SetVisibilityMessage);
278  char *messageData = new char[totalSize];
279  memset(messageData, 0, totalSize);
280  WindowManagerMessage *pWinMan =
281  reinterpret_cast<WindowManagerMessage *>(messageData);
282  SetVisibilityMessage *pMessage = reinterpret_cast<SetVisibilityMessage *>(
283  messageData + sizeof(WindowManagerMessage));
284 
285  // Fill the message.
286  pWinMan->messageCode = SetVisibility;
287  pWinMan->messageSize = sizeof(SetVisibilityMessage);
288  pWinMan->widgetHandle = m_Handle;
289  pWinMan->isResponse = false;
290  pMessage->bVisible = vis;
291 
292  // Transmit.
293  bool result = send(m_Socket, messageData, totalSize, 0) == totalSize;
294 
295  // Clean up.
296  delete[] messageData;
297 
298  return result;
299 }
300 
302 {
303  // Constructed yet (or already destroyed)?
304  if (!m_Handle)
305  return;
306 
307  // Allocate the message.
308  ssize_t totalSize = sizeof(WindowManagerMessage) + sizeof(DestroyMessage);
309  char *messageData = new char[totalSize];
310  memset(messageData, 0, totalSize);
311  WindowManagerMessage *pWinMan =
312  reinterpret_cast<WindowManagerMessage *>(messageData);
313 
314  // Fill the message.
315  pWinMan->messageCode = Destroy;
316  pWinMan->messageSize = sizeof(DestroyMessage);
317  pWinMan->widgetHandle = m_Handle;
318  pWinMan->isResponse = false;
319 
320  // Transmit.
322  send(m_Socket, messageData, totalSize, 0);
323 
324  // Clean up.
325  delete[] messageData;
326 
327  // Wait for an ACK message, before we return.
328  // At this point, we will be completely without a framebuffer.
329  Widget::checkForMessage(LibUiProtocol::Destroy, true);
330 
331  // Invalidate this widget now.
332  delete m_pFramebuffer;
333  delete m_SharedFramebuffer;
334  m_pFramebuffer = 0;
335  m_SharedFramebuffer = 0;
336  m_Handle = 0;
337 }
338 
339 void Widget::checkForEvents(bool bAsync)
340 {
341  bool bContinue = true;
342 
343  int max_fd = 0;
344  fd_set fds;
345  FD_ZERO(&fds);
346 
347  while (bContinue)
348  {
349  // Check for pending messages that we could handle easily.
351  return; // Messages were handled, we're done here.
352 
353  char *buffer = new char[4096];
354 
355  for (std::map<uint64_t, Widget *>::iterator it = s_KnownWidgets.begin();
356  it != s_KnownWidgets.end(); ++it)
357  {
358  Widget *pWidget = it->second;
359  max_fd = std::max(max_fd, pWidget->getSocket());
360  FD_SET(pWidget->getSocket(), &fds);
361  }
362 
363  struct timeval tv;
364  tv.tv_sec = tv.tv_usec = 0;
365 
366  // Async - check and don't do anything if no message found.
367  int nready = select(max_fd + 1, &fds, 0, 0, bAsync ? &tv : 0);
368  if (nready)
369  {
370  for (int i = 0; i <= max_fd; ++i)
371  {
372  if (FD_ISSET(i, &fds))
373  {
374  recv(i, buffer, 4096, 0);
375  bContinue = false;
376  break;
377  }
378  }
379  }
380  else if (bAsync)
381  {
382  delete[] buffer;
383  break;
384  }
385 
386  Widget::handleMessage(buffer);
387  delete[] buffer;
388  }
389 }
390 
391 void Widget::checkForMessage(size_t which, bool bResponse)
392 {
393  bool bContinue = true;
394 
395  // Check for one that might be hiding in the pending messages queue.
396  size_t messagesToCheck = s_PendingMessages.size();
397  for (; messagesToCheck > 0; --messagesToCheck)
398  {
399  char *buffer = s_PendingMessages.front();
400  s_PendingMessages.pop();
401 
403  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(buffer);
404  if (pHeader->messageCode == which && pHeader->isResponse == bResponse)
405  {
406  handleMessage(buffer);
407  delete[] buffer;
408  return;
409  }
410 
411  s_PendingMessages.push(buffer);
412  }
413 
414  int max_fd = 0;
415  fd_set fds;
416  FD_ZERO(&fds);
417 
418  while (bContinue)
419  {
420  char *buffer = new char[4096];
421 
422  for (std::map<uint64_t, Widget *>::iterator it = s_KnownWidgets.begin();
423  it != s_KnownWidgets.end(); ++it)
424  {
425  Widget *pWidget = it->second;
426  max_fd = std::max(max_fd, pWidget->getSocket());
427  FD_SET(pWidget->getSocket(), &fds);
428  }
429 
430  struct timeval tv;
431  tv.tv_sec = tv.tv_usec = 0;
432 
433  int nready = select(max_fd + 1, &fds, 0, 0, 0);
434  if (nready)
435  {
436  bool gotMessage = false;
437  for (int i = 0; i <= max_fd; ++i)
438  {
439  if (FD_ISSET(i, &fds))
440  {
441  recv(i, buffer, 4096, 0);
442  gotMessage = true;
443  break;
444  }
445  }
446 
447  if (!gotMessage)
448  {
449  delete[] buffer;
450  continue;
451  }
452  }
453  else
454  {
455  delete[] buffer;
456  continue;
457  }
458 
460  reinterpret_cast<LibUiProtocol::WindowManagerMessage *>(buffer);
461 
462  if (pHeader->messageCode == which && pHeader->isResponse == bResponse)
463  {
464  handleMessage(buffer);
465  bContinue = false;
466  delete[] buffer;
467  }
468  else
469  {
470  // Not what we wanted, mark the message pending and try again.
471  s_PendingMessages.push(buffer);
472  }
473  }
474 }
475 
476 void Widget::handleMessage(const char *pMessageBuffer)
477 {
478  const LibUiProtocol::WindowManagerMessage *pMessage =
479  reinterpret_cast<const LibUiProtocol::WindowManagerMessage *>(
480  pMessageBuffer);
481 
482  std::map<uint64_t, Widget *>::iterator it;
483  if ((it = s_KnownWidgets.find(pMessage->widgetHandle)) ==
484  s_KnownWidgets.end())
485  {
486  syslog(
487  LOG_ALERT, "no widget known for handle %lx",
488  pMessage->widgetHandle);
489  return;
490  }
491 
492  Widget *pWidget = it->second;
493  widgetCallback_t cb = pWidget->getCallback();
494  if (!cb)
495  {
496  return;
497  }
498 
499  switch (pMessage->messageCode)
500  {
501  case LibUiProtocol::Reposition:
502  {
503  const LibUiProtocol::RepositionMessage *pReposition =
504  reinterpret_cast<const LibUiProtocol::RepositionMessage *>(
505  pMessageBuffer +
507  delete pWidget->m_SharedFramebuffer;
508 #ifdef TARGET_LINUX
509  pWidget->m_SharedFramebuffer = new SharedBuffer(
510  pReposition->shmem_size, pReposition->shmem_handle);
511 #else
513  pReposition->shmem_size, pReposition->shmem_handle);
514  pWidget->m_SharedFramebuffer->initialise();
515 #endif
516 
517  // Run the callback now that the framebuffer is re-created.
518  cb(::Reposition, sizeof(pReposition->rt), &pReposition->rt);
519  break;
520  }
521  case LibUiProtocol::KeyEvent:
522  {
523  const LibUiProtocol::KeyEventMessage *pKeyEvent =
524  reinterpret_cast<const LibUiProtocol::KeyEventMessage *>(
525  pMessageBuffer +
527 
528  cb(::KeyUp, sizeof(pKeyEvent->key), &pKeyEvent->key);
529  break;
530  }
531  case LibUiProtocol::RawKeyEvent:
532  {
533  const LibUiProtocol::RawKeyEventMessage *pKeyEvent =
534  reinterpret_cast<const LibUiProtocol::RawKeyEventMessage *>(
535  pMessageBuffer +
537 
538  WidgetMessages msg = ::RawKeyUp;
539  if (pKeyEvent->state == LibUiProtocol::Down)
540  msg = ::RawKeyDown;
541 
542  cb(msg, sizeof(pKeyEvent->scancode), &pKeyEvent->scancode);
543  }
544  break;
545  case LibUiProtocol::Focus:
546  cb(::Focus, 0, 0);
547  break;
548  case LibUiProtocol::NoFocus:
549  cb(::NoFocus, 0, 0);
550  break;
551  case LibUiProtocol::RequestRedraw:
552  // Spurious redraw ACK.
553  break;
554  case LibUiProtocol::Destroy:
555  cb(::Terminate, 0, 0);
556  break;
557  default:
558  syslog(LOG_INFO, "** unknown event %d", pMessage->messageCode);
559  }
560 }
561 
563 {
564  bool bHandled = !s_PendingMessages.empty();
565  while (!s_PendingMessages.empty())
566  {
567  char *buffer = s_PendingMessages.front();
568  s_PendingMessages.pop();
569 
570  Widget::handleMessage(buffer);
571  delete[] buffer;
572  }
573  return bHandled;
574 }
PedigreeGraphics::Rect rt
New rect.
Definition: protocol.h:219
static bool handlePendingMessages()
Definition: Widget.cc:562
bool rigid
A &#39;rigid&#39; window cannot be resized by the window manager.
Definition: protocol.h:207
static void checkForEvents(bool bAsync=false)
Definition: Widget.cc:339
char endpoint[256]
IPC endpoint for this widget.
Definition: protocol.h:197
void * shmem_handle
New handle for the shared memory space.
Definition: protocol.h:222
static void checkForMessage(size_t which, bool bResponse=false)
Definition: Widget.cc:391
char title[256]
Initial title for this widget.
Definition: protocol.h:200
widgetCallback_t getCallback() const
Definition: Widget.h:179
Abstracts a buffer shared between multiple processes.
Definition: util.h:33
static std::map< uint64_t, Widget * > s_KnownWidgets
Definition: Widget.h:209
handle_t widgetHandle
Handle for the widget being referred to. Zero if no widget.
Definition: protocol.h:171
static size_t s_NumWidgets
Definition: Widget.h:215
bool redraw(PedigreeGraphics::Rect &rt)
Definition: Widget.cc:225
bool visibility(bool vis)
Definition: Widget.cc:269
PedigreeIpc::SharedIpcMessage * m_SharedFramebuffer
Definition: Widget.h:205
size_t messageSize
Size of the data in the message (after this header).
Definition: protocol.h:174
void destroy()
Definition: Widget.cc:301
size_t shmem_size
Size of the framebuffer.
Definition: protocol.h:225
size_t minWidth
Minimum width and height for the window.
Definition: protocol.h:203
static std::queue< char * > s_PendingMessages
Definition: Widget.h:212
Definition: Widget.h:63
bool construct(const char *endpoint, const char *title, widgetCallback_t cb, PedigreeGraphics::Rect &dimensions)
Definition: Widget.cc:90
MessageIdentifiers messageCode
Code of the message being sent.
Definition: protocol.h:168
static void handleMessage(const char *pMessageBuffer)
Definition: Widget.cc:476
bool setTitle(const std::string &newTitle)
Definition: Widget.cc:189
bool isResponse
Whether this message is a response from the window manager or not.
Definition: protocol.h:177
int getSocket() const
Definition: Widget.h:154