gtkdrv.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /**
  2. * @file gtk.c
  3. *
  4. */
  5. /*********************
  6. * INCLUDES
  7. *********************/
  8. #include "gtkdrv.h"
  9. #if USE_GTK
  10. #define _DEFAULT_SOURCE /* needed for usleep() */
  11. #include <stdlib.h>
  12. #include <unistd.h>
  13. #include <gtk/gtk.h>
  14. #include <gtk/gtkx.h>
  15. #include <pthread.h>
  16. #include <time.h>
  17. #include <sys/time.h>
  18. /*********************
  19. * DEFINES
  20. *********************/
  21. /**********************
  22. * TYPEDEFS
  23. **********************/
  24. /**********************
  25. * STATIC PROTOTYPES
  26. **********************/
  27. static void gtkdrv_handler(void * p);
  28. static gboolean mouse_pressed(GtkWidget *widget, GdkEventButton *event,
  29. gpointer user_data);
  30. static gboolean mouse_released(GtkWidget *widget, GdkEventButton *event,
  31. gpointer user_data);
  32. static gboolean mouse_motion(GtkWidget *widget, GdkEventMotion *event,
  33. gpointer user_data);
  34. static gboolean keyboard_press(GtkWidget *widget, GdkEventKey *event,
  35. gpointer user_data);
  36. static gboolean keyboard_release(GtkWidget *widget, GdkEventKey *event,
  37. gpointer user_data);
  38. static void quit_handler(void);
  39. /**********************
  40. * STATIC VARIABLES
  41. **********************/
  42. static GtkWidget *window;
  43. static GtkWidget *event_box;
  44. static GtkWidget *output_image;
  45. static GdkPixbuf *pixbuf;
  46. static unsigned char run_gtk;
  47. static lv_coord_t mouse_x;
  48. static lv_coord_t mouse_y;
  49. static lv_indev_state_t mouse_btn = LV_INDEV_STATE_REL;
  50. static lv_key_t last_key;
  51. static lv_indev_state_t last_key_state;
  52. static uint8_t fb[LV_HOR_RES_MAX * LV_VER_RES_MAX * 3];
  53. /**********************
  54. * MACROS
  55. **********************/
  56. /**********************
  57. * GLOBAL FUNCTIONS
  58. **********************/
  59. void gtkdrv_init(void)
  60. {
  61. // Init GTK
  62. gtk_init(NULL, NULL);
  63. /* Or just set up the widgets in code */
  64. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  65. gtk_window_set_default_size(GTK_WINDOW(window), LV_HOR_RES_MAX, LV_VER_RES_MAX);
  66. gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
  67. output_image = gtk_image_new();
  68. event_box = gtk_event_box_new (); // Use event_box around image, otherwise mouse position output in broadway is offset
  69. gtk_container_add(GTK_CONTAINER (event_box), output_image);
  70. gtk_container_add(GTK_CONTAINER (window), event_box);
  71. gtk_widget_add_events(event_box, GDK_BUTTON_PRESS_MASK);
  72. gtk_widget_add_events(event_box, GDK_SCROLL_MASK);
  73. gtk_widget_add_events(event_box, GDK_POINTER_MOTION_MASK);
  74. gtk_widget_add_events(window, GDK_KEY_PRESS_MASK);
  75. g_signal_connect(window, "destroy", G_CALLBACK(quit_handler), NULL);
  76. g_signal_connect(event_box, "button-press-event", G_CALLBACK(mouse_pressed), NULL);
  77. g_signal_connect(event_box, "button-release-event", G_CALLBACK(mouse_released), NULL);
  78. g_signal_connect(event_box, "motion-notify-event", G_CALLBACK(mouse_motion), NULL);
  79. g_signal_connect(window, "key_press_event", G_CALLBACK(keyboard_press), NULL);
  80. g_signal_connect(window, "key_release_event", G_CALLBACK(keyboard_release), NULL);
  81. gtk_widget_show_all(window);
  82. pixbuf = gdk_pixbuf_new_from_data((guchar*)fb, GDK_COLORSPACE_RGB, false, 8, LV_HOR_RES_MAX, LV_VER_RES_MAX, LV_HOR_RES_MAX * 3, NULL, NULL);
  83. if (pixbuf == NULL)
  84. {
  85. fprintf(stderr, "Creating pixbuf failed\n");
  86. return;
  87. }
  88. pthread_t thread;
  89. pthread_create(&thread, NULL, gtkdrv_handler, NULL);
  90. }
  91. /*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
  92. uint32_t gtkdrv_tick_get(void)
  93. {
  94. static uint64_t start_ms = 0;
  95. if(start_ms == 0) {
  96. struct timeval tv_start;
  97. gettimeofday(&tv_start, NULL);
  98. start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
  99. }
  100. struct timeval tv_now;
  101. gettimeofday(&tv_now, NULL);
  102. uint64_t now_ms;
  103. now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
  104. uint32_t time_ms = now_ms - start_ms;
  105. return time_ms;
  106. }
  107. /**
  108. * Flush a buffer to the marked area
  109. * @param drv pointer to driver where this function belongs
  110. * @param area an area where to copy `color_p`
  111. * @param color_p an array of pixel to copy to the `area` part of the screen
  112. */
  113. void gtkdrv_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
  114. {
  115. lv_coord_t hres = disp_drv->rotated == 0 ? disp_drv->hor_res : disp_drv->ver_res;
  116. lv_coord_t vres = disp_drv->rotated == 0 ? disp_drv->ver_res : disp_drv->hor_res;
  117. /*Return if the area is out the screen*/
  118. if(area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1) {
  119. lv_disp_flush_ready(disp_drv);
  120. return;
  121. }
  122. int32_t y;
  123. int32_t x;
  124. int32_t p;
  125. for(y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++) {
  126. p = (y * disp_drv->hor_res + area->x1) * 3;
  127. for(x = area->x1; x <= area->x2 && x < disp_drv->hor_res; x++) {
  128. fb[p] = color_p->ch.red;
  129. fb[p + 1] = color_p->ch.green;
  130. fb[p + 2] = color_p->ch.blue;
  131. p += 3;
  132. color_p ++;
  133. }
  134. }
  135. /*IMPORTANT! It must be called to tell the system the flush is ready*/
  136. lv_disp_flush_ready(disp_drv);
  137. }
  138. void gtkdrv_mouse_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data)
  139. {
  140. data->point.x = mouse_x;
  141. data->point.y = mouse_y;
  142. data->state = mouse_btn;
  143. }
  144. void gtkdrv_keyboard_read_cb(lv_indev_drv_t * drv, lv_indev_data_t * data)
  145. {
  146. data->key = last_key;
  147. data->state = last_key_state;
  148. }
  149. /**********************
  150. * STATIC FUNCTIONS
  151. **********************/
  152. static void gtkdrv_handler(void * p)
  153. {
  154. while(1) {
  155. gtk_image_set_from_pixbuf(GTK_IMAGE(output_image), pixbuf); // Test code
  156. /* Real code should: call gdk_pixbuf_new_from_data () with pointer to frame buffer
  157. generated by LVGL. See
  158. https://developer.gnome.org/gdk-pixbuf/2.36/gdk-pixbuf-Image-Data-in-Memory.html
  159. */
  160. gtk_main_iteration_do(FALSE);
  161. /* Explicitly calling each iteration of the GTK main loop allows LVGL to sync frame
  162. buffer updates with GTK. It is perhaps also possible to just call gtk_main(), but not
  163. sure how sync will work then
  164. */
  165. usleep(1*1000);
  166. }
  167. }
  168. static gboolean mouse_pressed(GtkWidget *widget, GdkEventButton *event,
  169. gpointer user_data)
  170. {
  171. mouse_btn = LV_INDEV_STATE_PR;
  172. // Important, if this function returns TRUE the window cannot be moved around inside the browser
  173. // when using broadway
  174. return FALSE;
  175. }
  176. static gboolean mouse_released(GtkWidget *widget, GdkEventButton *event,
  177. gpointer user_data)
  178. {
  179. mouse_btn = LV_INDEV_STATE_REL;
  180. // Important, if this function returns TRUE the window cannot be moved around inside the browser
  181. // when using broadway
  182. return FALSE;
  183. }
  184. /*****************************************************************************/
  185. static gboolean mouse_motion(GtkWidget *widget, GdkEventMotion *event,
  186. gpointer user_data)
  187. {
  188. mouse_x = event->x;
  189. mouse_y = event->y;
  190. // Important, if this function returns TRUE the window cannot be moved around inside the browser
  191. // when using broadway
  192. return FALSE;
  193. }
  194. static gboolean keyboard_press(GtkWidget *widget, GdkEventKey *event,
  195. gpointer user_data)
  196. {
  197. uint32_t ascii_key = event->keyval;
  198. /*Remap some key to LV_KEY_... to manage groups*/
  199. switch(event->keyval) {
  200. case GDK_KEY_rightarrow:
  201. case GDK_KEY_Right:
  202. ascii_key = LV_KEY_RIGHT;
  203. break;
  204. case GDK_KEY_leftarrow:
  205. case GDK_KEY_Left:
  206. ascii_key = LV_KEY_LEFT;
  207. break;
  208. case GDK_KEY_uparrow:
  209. case GDK_KEY_Up:
  210. ascii_key = LV_KEY_UP;
  211. break;
  212. case GDK_KEY_downarrow:
  213. case GDK_KEY_Down:
  214. ascii_key = LV_KEY_DOWN;
  215. break;
  216. case GDK_KEY_Escape:
  217. ascii_key = LV_KEY_ESC;
  218. break;
  219. case GDK_KEY_BackSpace:
  220. ascii_key = LV_KEY_BACKSPACE;
  221. break;
  222. case GDK_KEY_Delete:
  223. ascii_key = LV_KEY_DEL;
  224. break;
  225. case GDK_KEY_Tab:
  226. ascii_key = LV_KEY_NEXT;
  227. break;
  228. case GDK_KEY_KP_Enter:
  229. case GDK_KEY_Return:
  230. case '\r':
  231. ascii_key = LV_KEY_ENTER;
  232. break;
  233. default:
  234. break;
  235. }
  236. last_key = ascii_key;
  237. last_key_state = LV_INDEV_STATE_PR;
  238. // For other codes refer to https://developer.gnome.org/gdk3/stable/gdk3-Event-Structures.html#GdkEventKey
  239. return TRUE;
  240. }
  241. static gboolean keyboard_release(GtkWidget *widget, GdkEventKey *event,
  242. gpointer user_data)
  243. {
  244. last_key = 0;
  245. last_key_state = LV_INDEV_STATE_REL;
  246. // For other codes refer to https://developer.gnome.org/gdk3/stable/gdk3-Event-Structures.html#GdkEventKey
  247. return TRUE;
  248. }
  249. static void quit_handler(void)
  250. {
  251. exit(0);
  252. run_gtk = FALSE;
  253. }
  254. #endif /*USE_GTK*/