GEventLoop.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #include "GEventLoop.h"
  2. #include "GEvent.h"
  3. #include "GWindow.h"
  4. #include <LibC/errno.h>
  5. #include <LibC/fcntl.h>
  6. #include <LibC/stdio.h>
  7. #include <LibC/stdlib.h>
  8. #include <LibC/string.h>
  9. #include <LibC/sys/select.h>
  10. #include <LibC/sys/socket.h>
  11. #include <LibC/sys/time.h>
  12. #include <LibC/time.h>
  13. #include <LibC/unistd.h>
  14. #include <LibCore/CNotifier.h>
  15. #include <LibCore/CObject.h>
  16. #include <LibGUI/GAction.h>
  17. #include <LibGUI/GApplication.h>
  18. #include <LibGUI/GClipboard.h>
  19. #include <LibGUI/GDesktop.h>
  20. #include <LibGUI/GMenu.h>
  21. #include <LibGUI/GWidget.h>
  22. #include <sys/uio.h>
  23. //#define GEVENTLOOP_DEBUG
  24. //#define COALESCING_DEBUG
  25. GWindowServerConnection& GWindowServerConnection::the()
  26. {
  27. static GWindowServerConnection* s_connection = nullptr;
  28. if (!s_connection) {
  29. s_connection = new GWindowServerConnection();
  30. s_connection->handshake();
  31. }
  32. return *s_connection;
  33. }
  34. void GWindowServerConnection::handshake()
  35. {
  36. WSAPI_ClientMessage request;
  37. request.type = WSAPI_ClientMessage::Type::Greeting;
  38. request.greeting.client_pid = getpid();
  39. auto response = sync_request(request, WSAPI_ServerMessage::Type::Greeting);
  40. handle_greeting(response);
  41. }
  42. GEventLoop::GEventLoop()
  43. {
  44. // ensure the WS connection is up, as our users might be expecting it to be
  45. // valid very early (via e.g. GDesktop) :)
  46. GWindowServerConnection::the();
  47. }
  48. GEventLoop::~GEventLoop()
  49. {
  50. }
  51. void GWindowServerConnection::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window, const ByteBuffer& extra_data)
  52. {
  53. #ifdef GEVENTLOOP_DEBUG
  54. dbgprintf("WID=%x Paint\n", event.window_id);
  55. #endif
  56. Vector<Rect, 32> rects;
  57. for (int i = 0; i < min(WSAPI_ServerMessage::max_inline_rect_count, event.rect_count); ++i)
  58. rects.append(event.rects[i]);
  59. if (event.extra_size) {
  60. auto* extra_rects = reinterpret_cast<const WSAPI_Rect*>(extra_data.data());
  61. for (int i = 0; i < event.rect_count - WSAPI_ServerMessage::max_inline_rect_count; ++i)
  62. rects.append(extra_rects[i]);
  63. }
  64. CEventLoop::current().post_event(window, make<GMultiPaintEvent>(rects, event.paint.window_size));
  65. }
  66. void GWindowServerConnection::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window)
  67. {
  68. CEventLoop::current().post_event(window, make<GResizeEvent>(event.window.old_rect.size, event.window.rect.size));
  69. }
  70. void GWindowServerConnection::handle_window_activation_event(const WSAPI_ServerMessage& event, GWindow& window)
  71. {
  72. #ifdef GEVENTLOOP_DEBUG
  73. dbgprintf("WID=%x WindowActivation\n", event.window_id);
  74. #endif
  75. CEventLoop::current().post_event(window, make<GEvent>(event.type == WSAPI_ServerMessage::Type::WindowActivated ? GEvent::WindowBecameActive : GEvent::WindowBecameInactive));
  76. }
  77. void GWindowServerConnection::handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow& window)
  78. {
  79. CEventLoop::current().post_event(window, make<GEvent>(GEvent::WindowCloseRequest));
  80. }
  81. void GWindowServerConnection::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window)
  82. {
  83. CEventLoop::current().post_event(window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft));
  84. }
  85. void GWindowServerConnection::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window)
  86. {
  87. #ifdef GEVENTLOOP_DEBUG
  88. dbgprintf("WID=%x KeyEvent character=0x%b\n", event.window_id, event.key.character);
  89. #endif
  90. auto key_event = make<GKeyEvent>(event.type == WSAPI_ServerMessage::Type::KeyDown ? GEvent::KeyDown : GEvent::KeyUp, event.key.key, event.key.modifiers);
  91. if (event.key.character != '\0')
  92. key_event->m_text = String(&event.key.character, 1);
  93. if (event.type == WSAPI_ServerMessage::Type::KeyDown) {
  94. if (auto* focused_widget = window.focused_widget()) {
  95. if (auto* action = focused_widget->action_for_key_event(*key_event)) {
  96. if (action->is_enabled()) {
  97. action->activate();
  98. return;
  99. }
  100. }
  101. }
  102. if (auto* action = GApplication::the().action_for_key_event(*key_event)) {
  103. if (action->is_enabled()) {
  104. action->activate();
  105. return;
  106. }
  107. }
  108. }
  109. CEventLoop::current().post_event(window, move(key_event));
  110. }
  111. void GWindowServerConnection::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window)
  112. {
  113. #ifdef GEVENTLOOP_DEBUG
  114. dbgprintf("WID=%x MouseEvent %d,%d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y, event.mouse.wheel_delta);
  115. #endif
  116. GMouseEvent::Type type;
  117. switch (event.type) {
  118. case WSAPI_ServerMessage::Type::MouseMove:
  119. type = GEvent::MouseMove;
  120. break;
  121. case WSAPI_ServerMessage::Type::MouseUp:
  122. type = GEvent::MouseUp;
  123. break;
  124. case WSAPI_ServerMessage::Type::MouseDown:
  125. type = GEvent::MouseDown;
  126. break;
  127. case WSAPI_ServerMessage::Type::MouseDoubleClick:
  128. type = GEvent::MouseDoubleClick;
  129. break;
  130. case WSAPI_ServerMessage::Type::MouseWheel:
  131. type = GEvent::MouseWheel;
  132. break;
  133. default:
  134. ASSERT_NOT_REACHED();
  135. break;
  136. }
  137. GMouseButton button { GMouseButton::None };
  138. switch (event.mouse.button) {
  139. case WSAPI_MouseButton::NoButton:
  140. button = GMouseButton::None;
  141. break;
  142. case WSAPI_MouseButton::Left:
  143. button = GMouseButton::Left;
  144. break;
  145. case WSAPI_MouseButton::Right:
  146. button = GMouseButton::Right;
  147. break;
  148. case WSAPI_MouseButton::Middle:
  149. button = GMouseButton::Middle;
  150. break;
  151. default:
  152. ASSERT_NOT_REACHED();
  153. break;
  154. }
  155. CEventLoop::current().post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta));
  156. }
  157. void GWindowServerConnection::handle_menu_event(const WSAPI_ServerMessage& event)
  158. {
  159. if (event.type == WSAPI_ServerMessage::Type::MenuItemActivated) {
  160. auto* menu = GMenu::from_menu_id(event.menu.menu_id);
  161. if (!menu) {
  162. dbgprintf("GEventLoop received event for invalid window ID %d\n", event.window_id);
  163. return;
  164. }
  165. if (auto* action = menu->action_at(event.menu.identifier))
  166. action->activate();
  167. return;
  168. }
  169. ASSERT_NOT_REACHED();
  170. }
  171. void GWindowServerConnection::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window)
  172. {
  173. #ifdef GEVENTLOOP_DEBUG
  174. dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type);
  175. #endif
  176. if (event.type == WSAPI_ServerMessage::WM_WindowStateChanged)
  177. CEventLoop::current().post_event(window, make<GWMWindowStateChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect, event.wm.is_active, (GWindowType)event.wm.window_type, event.wm.is_minimized));
  178. else if (event.type == WSAPI_ServerMessage::WM_WindowRectChanged)
  179. CEventLoop::current().post_event(window, make<GWMWindowRectChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.rect));
  180. else if (event.type == WSAPI_ServerMessage::WM_WindowIconBitmapChanged)
  181. CEventLoop::current().post_event(window, make<GWMWindowIconBitmapChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.icon_buffer_id, event.wm.icon_size));
  182. else if (event.type == WSAPI_ServerMessage::WM_WindowRemoved)
  183. CEventLoop::current().post_event(window, make<GWMWindowRemovedEvent>(event.wm.client_id, event.wm.window_id));
  184. else
  185. ASSERT_NOT_REACHED();
  186. }
  187. void GWindowServerConnection::postprocess_bundles(Vector<IncomingMessageBundle>& bundles)
  188. {
  189. int coalesced_paints = 0;
  190. int coalesced_resizes = 0;
  191. auto unprocessed_bundles = move(bundles);
  192. HashMap<int, Size> latest_size_for_window_id;
  193. for (auto& bundle : unprocessed_bundles) {
  194. auto& event = bundle.message;
  195. if (event.type == WSAPI_ServerMessage::Type::WindowResized) {
  196. latest_size_for_window_id.set(event.window_id, event.window.rect.size);
  197. }
  198. }
  199. int paint_count = 0;
  200. HashMap<int, Size> latest_paint_size_for_window_id;
  201. for (auto& bundle : unprocessed_bundles) {
  202. auto& event = bundle.message;
  203. if (event.type == WSAPI_ServerMessage::Type::Paint) {
  204. ++paint_count;
  205. #ifdef COALESCING_DEBUG
  206. dbgprintf(" (window: %s)\n", Size(event.paint.window_size).to_string().characters());
  207. #endif
  208. latest_paint_size_for_window_id.set(event.window_id, event.paint.window_size);
  209. }
  210. }
  211. #ifdef COALESCING_DEBUG
  212. dbgprintf("paint_count: %d\n", paint_count);
  213. #endif
  214. for (auto& bundle : unprocessed_bundles) {
  215. auto& event = bundle.message;
  216. if (event.type == WSAPI_ServerMessage::Type::Greeting) {
  217. // Shouldn't get a second greeting
  218. dbg() << "Got second Greeting!?";
  219. ASSERT_NOT_REACHED();
  220. continue;
  221. }
  222. if (event.type == WSAPI_ServerMessage::Type::ScreenRectChanged) {
  223. GDesktop::the().did_receive_screen_rect({}, event.screen.rect);
  224. continue;
  225. }
  226. if (event.type == WSAPI_ServerMessage::Type::ClipboardContentsChanged) {
  227. GClipboard::the().did_receive_clipboard_contents_changed({}, String(event.text, event.text_length));
  228. continue;
  229. }
  230. if (event.type == WSAPI_ServerMessage::Error) {
  231. dbgprintf("GEventLoop got error message from server\n");
  232. dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters());
  233. CEventLoop::current().quit(1);
  234. return;
  235. }
  236. switch (event.type) {
  237. case WSAPI_ServerMessage::MenuItemActivated:
  238. handle_menu_event(event);
  239. continue;
  240. default:
  241. break;
  242. }
  243. auto* window = GWindow::from_window_id(event.window_id);
  244. if (!window) {
  245. dbgprintf("GEventLoop received event for invalid window ID %d\n", event.window_id);
  246. continue;
  247. }
  248. switch (event.type) {
  249. case WSAPI_ServerMessage::Type::Paint:
  250. if (Size(event.paint.window_size) != latest_paint_size_for_window_id.get(event.window_id).value_or({})) {
  251. ++coalesced_paints;
  252. break;
  253. }
  254. handle_paint_event(event, *window, bundle.extra_data);
  255. break;
  256. case WSAPI_ServerMessage::Type::MouseDown:
  257. case WSAPI_ServerMessage::Type::MouseDoubleClick:
  258. case WSAPI_ServerMessage::Type::MouseUp:
  259. case WSAPI_ServerMessage::Type::MouseMove:
  260. case WSAPI_ServerMessage::Type::MouseWheel:
  261. handle_mouse_event(event, *window);
  262. break;
  263. case WSAPI_ServerMessage::Type::WindowActivated:
  264. case WSAPI_ServerMessage::Type::WindowDeactivated:
  265. handle_window_activation_event(event, *window);
  266. break;
  267. case WSAPI_ServerMessage::Type::WindowCloseRequest:
  268. handle_window_close_request_event(event, *window);
  269. break;
  270. case WSAPI_ServerMessage::Type::KeyDown:
  271. case WSAPI_ServerMessage::Type::KeyUp:
  272. handle_key_event(event, *window);
  273. break;
  274. case WSAPI_ServerMessage::Type::WindowEntered:
  275. case WSAPI_ServerMessage::Type::WindowLeft:
  276. handle_window_entered_or_left_event(event, *window);
  277. break;
  278. case WSAPI_ServerMessage::Type::WindowResized:
  279. if (Size(event.window.rect.size) != latest_size_for_window_id.get(event.window_id).value_or({})) {
  280. ++coalesced_resizes;
  281. break;
  282. }
  283. handle_resize_event(event, *window);
  284. break;
  285. default:
  286. if (event.type > WSAPI_ServerMessage::__Begin_WM_Events__ && event.type < WSAPI_ServerMessage::Type::__End_WM_Events__)
  287. handle_wm_event(event, *window);
  288. break;
  289. }
  290. }
  291. #ifdef COALESCING_DEBUG
  292. if (coalesced_paints)
  293. dbgprintf("Coalesced %d paints\n", coalesced_paints);
  294. if (coalesced_resizes)
  295. dbgprintf("Coalesced %d resizes\n", coalesced_resizes);
  296. #endif
  297. }
  298. void GWindowServerConnection::handle_greeting(WSAPI_ServerMessage& message)
  299. {
  300. set_server_pid(message.greeting.server_pid);
  301. set_my_client_id(message.greeting.your_client_id);
  302. GDesktop::the().did_receive_screen_rect({}, message.greeting.screen_rect);
  303. }