GWindow.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #include "GWindow.h"
  2. #include "GEvent.h"
  3. #include "GEventLoop.h"
  4. #include "GWidget.h"
  5. #include <SharedGraphics/GraphicsBitmap.h>
  6. #include <LibC/stdio.h>
  7. #include <LibC/stdlib.h>
  8. #include <LibC/unistd.h>
  9. #include <AK/HashMap.h>
  10. //#define UPDATE_COALESCING_DEBUG
  11. static HashMap<int, GWindow*>* s_windows;
  12. static HashMap<int, GWindow*>& windows()
  13. {
  14. if (!s_windows)
  15. s_windows = new HashMap<int, GWindow*>;
  16. return *s_windows;
  17. }
  18. GWindow* GWindow::from_window_id(int window_id)
  19. {
  20. auto it = windows().find(window_id);
  21. if (it != windows().end())
  22. return (*it).value;
  23. return nullptr;
  24. }
  25. GWindow::GWindow(GObject* parent)
  26. : GObject(parent)
  27. {
  28. m_rect_when_windowless = { 100, 400, 140, 140 };
  29. m_title_when_windowless = "GWindow";
  30. }
  31. GWindow::~GWindow()
  32. {
  33. if (m_main_widget)
  34. delete m_main_widget;
  35. hide();
  36. }
  37. void GWindow::close()
  38. {
  39. // FIXME: If we exit the event loop, we're never gonna deal with the delete_later request!
  40. // This will become relevant once we support nested event loops.
  41. if (should_exit_app_on_close())
  42. GEventLoop::main().quit(0);
  43. delete_later();
  44. }
  45. void GWindow::show()
  46. {
  47. if (m_window_id)
  48. return;
  49. WSAPI_ClientMessage request;
  50. request.type = WSAPI_ClientMessage::Type::CreateWindow;
  51. request.window_id = m_window_id;
  52. request.window.rect = m_rect_when_windowless;
  53. ASSERT(m_title_when_windowless.length() < sizeof(request.text));
  54. strcpy(request.text, m_title_when_windowless.characters());
  55. request.text_length = m_title_when_windowless.length();
  56. auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow);
  57. m_window_id = response.window_id;
  58. windows().set(m_window_id, this);
  59. update();
  60. }
  61. void GWindow::hide()
  62. {
  63. if (!m_window_id)
  64. return;
  65. windows().remove(m_window_id);
  66. WSAPI_ClientMessage request;
  67. request.type = WSAPI_ClientMessage::Type::DestroyWindow;
  68. request.window_id = m_window_id;
  69. ASSERT(m_title_when_windowless.length() < sizeof(request.text));
  70. strcpy(request.text, m_title_when_windowless.characters());
  71. request.text_length = m_title_when_windowless.length();
  72. GEventLoop::main().post_message_to_server(request);
  73. }
  74. void GWindow::set_title(String&& title)
  75. {
  76. m_title_when_windowless = move(title);
  77. if (!m_window_id)
  78. return;
  79. WSAPI_ClientMessage request;
  80. request.type = WSAPI_ClientMessage::Type::SetWindowTitle;
  81. request.window_id = m_window_id;
  82. ASSERT(m_title_when_windowless.length() < sizeof(request.text));
  83. strcpy(request.text, m_title_when_windowless.characters());
  84. request.text_length = m_title_when_windowless.length();
  85. GEventLoop::main().post_message_to_server(request);
  86. }
  87. String GWindow::title() const
  88. {
  89. if (!m_window_id)
  90. return m_title_when_windowless;
  91. WSAPI_ClientMessage request;
  92. request.type = WSAPI_ClientMessage::Type::GetWindowTitle;
  93. request.window_id = m_window_id;
  94. ASSERT(m_title_when_windowless.length() < sizeof(request.text));
  95. strcpy(request.text, m_title_when_windowless.characters());
  96. request.text_length = m_title_when_windowless.length();
  97. auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle);
  98. return String(response.text, response.text_length);
  99. }
  100. Rect GWindow::rect() const
  101. {
  102. if (!m_window_id)
  103. return m_rect_when_windowless;
  104. WSAPI_ClientMessage request;
  105. request.type = WSAPI_ClientMessage::Type::GetWindowRect;
  106. request.window_id = m_window_id;
  107. auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect);
  108. ASSERT(response.window_id == m_window_id);
  109. return response.window.rect;
  110. }
  111. void GWindow::set_rect(const Rect& a_rect)
  112. {
  113. m_rect_when_windowless = a_rect;
  114. if (!m_window_id)
  115. return;
  116. WSAPI_ClientMessage request;
  117. request.type = WSAPI_ClientMessage::Type::SetWindowRect;
  118. request.window_id = m_window_id;
  119. request.window.rect = a_rect;
  120. GEventLoop::main().post_message_to_server(request);
  121. }
  122. void GWindow::event(GEvent& event)
  123. {
  124. if (event.is_mouse_event()) {
  125. if (m_global_cursor_tracking_widget) {
  126. // FIXME: This won't work for widgets-within-widgets.
  127. auto& mouse_event = static_cast<GMouseEvent&>(event);
  128. Point local_point { mouse_event.x() - m_global_cursor_tracking_widget->relative_rect().x(), mouse_event.y() - m_global_cursor_tracking_widget->relative_rect().y() };
  129. auto local_event = make<GMouseEvent>(event.type(), local_point, mouse_event.buttons(), mouse_event.button());
  130. m_global_cursor_tracking_widget->event(*local_event);
  131. }
  132. if (!m_main_widget)
  133. return;
  134. auto& mouse_event = static_cast<GMouseEvent&>(event);
  135. if (m_main_widget) {
  136. auto result = m_main_widget->hit_test(mouse_event.x(), mouse_event.y());
  137. auto local_event = make<GMouseEvent>(event.type(), Point { result.localX, result.localY }, mouse_event.buttons(), mouse_event.button());
  138. ASSERT(result.widget);
  139. return result.widget->event(*local_event);
  140. }
  141. return;
  142. }
  143. if (event.is_paint_event()) {
  144. m_pending_paint_event_rects.clear();
  145. if (!m_main_widget)
  146. return;
  147. auto& paint_event = static_cast<GPaintEvent&>(event);
  148. auto rect = paint_event.rect();
  149. if (rect.is_empty())
  150. rect = m_main_widget->rect();
  151. m_main_widget->event(*make<GPaintEvent>(rect));
  152. if (m_window_id) {
  153. WSAPI_ClientMessage message;
  154. message.type = WSAPI_ClientMessage::Type::DidFinishPainting;
  155. message.window_id = m_window_id;
  156. message.window.rect = rect;
  157. GEventLoop::main().post_message_to_server(message);
  158. }
  159. return;
  160. }
  161. if (event.is_key_event()) {
  162. if (m_focused_widget)
  163. return m_focused_widget->event(event);
  164. if (m_main_widget)
  165. return m_main_widget->event(event);
  166. return;
  167. }
  168. if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) {
  169. m_is_active = event.type() == GEvent::WindowBecameActive;
  170. if (m_main_widget)
  171. m_main_widget->event(event);
  172. if (m_focused_widget)
  173. m_focused_widget->update();
  174. return;
  175. }
  176. if (event.type() == GEvent::WindowCloseRequest) {
  177. close();
  178. return;
  179. }
  180. GObject::event(event);
  181. }
  182. bool GWindow::is_visible() const
  183. {
  184. return false;
  185. }
  186. void GWindow::update(const Rect& a_rect)
  187. {
  188. if (!m_window_id)
  189. return;
  190. for (auto& pending_rect : m_pending_paint_event_rects) {
  191. if (pending_rect.contains(a_rect)) {
  192. #ifdef UPDATE_COALESCING_DEBUG
  193. dbgprintf("Ignoring %s since it's contained by pending rect %s\n", a_rect.to_string().characters(), pending_rect.to_string().characters());
  194. #endif
  195. return;
  196. }
  197. }
  198. m_pending_paint_event_rects.append(a_rect);
  199. WSAPI_ClientMessage request;
  200. request.type = WSAPI_ClientMessage::Type::InvalidateRect;
  201. request.window_id = m_window_id;
  202. request.window.rect = a_rect;
  203. GEventLoop::main().post_message_to_server(request);
  204. }
  205. void GWindow::set_main_widget(GWidget* widget)
  206. {
  207. if (m_main_widget == widget)
  208. return;
  209. m_main_widget = widget;
  210. if (m_main_widget) {
  211. auto new_window_rect = rect();
  212. if (m_main_widget->horizontal_size_policy() == SizePolicy::Fixed)
  213. new_window_rect.set_width(m_main_widget->preferred_size().width());
  214. if (m_main_widget->vertical_size_policy() == SizePolicy::Fixed)
  215. new_window_rect.set_height(m_main_widget->preferred_size().height());
  216. set_rect(new_window_rect);
  217. m_main_widget->set_relative_rect({ { }, new_window_rect.size() });
  218. m_main_widget->set_window(this);
  219. if (m_main_widget->accepts_focus())
  220. m_main_widget->set_focus(true);
  221. }
  222. update();
  223. }
  224. void GWindow::set_focused_widget(GWidget* widget)
  225. {
  226. if (m_focused_widget == widget)
  227. return;
  228. if (m_focused_widget) {
  229. GEventLoop::main().post_event(m_focused_widget, make<GEvent>(GEvent::FocusOut));
  230. m_focused_widget->update();
  231. }
  232. m_focused_widget = widget;
  233. if (m_focused_widget) {
  234. GEventLoop::main().post_event(m_focused_widget, make<GEvent>(GEvent::FocusIn));
  235. m_focused_widget->update();
  236. }
  237. }
  238. void GWindow::set_global_cursor_tracking_widget(GWidget* widget)
  239. {
  240. ASSERT(m_window_id);
  241. if (widget == m_global_cursor_tracking_widget.ptr())
  242. return;
  243. m_global_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr;
  244. WSAPI_ClientMessage request;
  245. request.type = WSAPI_ClientMessage::Type::SetGlobalCursorTracking;
  246. request.window_id = m_window_id;
  247. request.value = widget != nullptr;
  248. // FIXME: What if the cursor moves out of our interest range before the server can handle this?
  249. // Maybe there could be a response that includes the current cursor location as of enabling.
  250. GEventLoop::main().post_message_to_server(request);
  251. }