Edit

kc3-lang/kc3/libc3_window/cairo/xcb/window_cairo_xcb.c

Branch :

  • Show log

    Commit

  • Author : thodg Thomas de Grivel
    Date : 2024-03-24 14:53:08
    Hash : 75270fe7
    Message : move libc3/window to libc3_window, shorter build messages and build log for libc3 (see configure)

  • libc3_window/cairo/xcb/window_cairo_xcb.c
  • /* c3
     * Copyright 2022-2024 kmx.io <contact@kmx.io>
     *
     * Permission is hereby granted to use this software granted the above
     * copyright notice and this permission paragraph are included in all
     * copies and substantial portions of this software.
     *
     * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
     * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
     * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
     * THIS SOFTWARE.
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <cairo/cairo-xcb.h>
    #include <libc3/c3.h>
    #include <xcb/xcb.h>
    #include <xkbcommon/xkbcommon.h>
    #include <xkbcommon/xkbcommon-x11.h>
    #include "window_cairo_xcb.h"
    
    bool window_cairo_run (s_window_cairo *window)
    {
      return window_cairo_xcb_run(window);
    }
    
    bool window_cairo_xcb_event (s_window_cairo *window,
                                 xcb_connection_t *conn,
                                 xcb_screen_t *screen,
                                 xcb_visualtype_t *visual,
                                 xcb_pixmap_t *pixmap,
                                 cairo_surface_t **surface,
                                 xcb_window_t xcb_window,
                                 xcb_generic_event_t *event,
                                 struct xkb_state *xkb_state)
    {
      xcb_button_press_event_t     *event_button;
      xcb_configure_notify_event_t *event_config;
      xcb_key_press_event_t        *event_key;
      xcb_motion_notify_event_t    *event_motion;
      switch (event->response_type & ~0x80) {
      case XCB_BUTTON_PRESS:
        event_button = (xcb_button_press_event_t *) event;
        if (! window->button(window, event_button->detail,
                             event_button->event_x,
                             event_button->event_y))
          goto ko;
        break;
      case XCB_EXPOSE:
        if (! window->render(window))
          goto ko;
        xcb_gcontext_t gc = xcb_generate_id(conn);
        u32 gc_mask = XCB_GC_GRAPHICS_EXPOSURES;
        u32 gc_values[1] = {0};
        xcb_create_gc(conn, gc, xcb_window, gc_mask, gc_values);
        xcb_copy_area(conn, *pixmap, xcb_window, gc,
                      0, 0, 0, 0, window->w, window->h);
        xcb_flush(conn);
        break;
      case XCB_CONFIGURE_NOTIFY:
        event_config = (xcb_configure_notify_event_t *) event;
        if (! window->resize(window, event_config->width,
                             event_config->height))
          goto ko;
        window->w = event_config->width;
        window->h = event_config->height;
        cairo_destroy(window->cr);
        cairo_surface_destroy(*surface);
        xcb_free_pixmap(conn, *pixmap);
        *pixmap = xcb_generate_id(conn);
        xcb_create_pixmap(conn, screen->root_depth, *pixmap, xcb_window,
                          window->w, window->h);
        *surface = cairo_xcb_surface_create(conn, *pixmap, visual, window->w,
                                            window->h);
        window->cr = cairo_create(*surface);
        break;
      case XCB_KEY_PRESS:
        event_key = (xcb_key_press_event_t *) event;
        xkb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, event_key->detail);
        if (! window->key(window, sym))
          goto ko;
        break;
      case XCB_MOTION_NOTIFY:
        event_motion = (xcb_motion_notify_event_t *) event;
        if (! window->motion(window, event_motion->event_x,
                             event_motion->event_y))
          goto ko;
        break;
      default:
        printf("event type %d\n", event->response_type & ~0x80);
      }
      free(event);
      return true;
     ko:
      free(event);
      return false;
    }
    
    bool window_cairo_xcb_run (s_window_cairo *window)
    {
      xcb_connection_t *conn;
      xcb_generic_event_t *event;
      xcb_pixmap_t pixmap;
      bool r;
      xcb_screen_t *screen;
      xcb_visualtype_t *screen_visual;
      s_time sleep;
      cairo_surface_t *surface;
      u32 value_mask;
      u32 *value_list;
      xcb_window_t xcb_window;
      struct xkb_context *xkb_ctx;
      int32_t xkb_device_id;
      struct xkb_keymap *xkb_keymap;
      struct xkb_state *xkb_state;
      conn = xcb_connect(NULL, NULL);
      if (xcb_connection_has_error(conn)) {
        fprintf(stderr, "Error opening display.\n");
        return false;
      }
      xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
      if (!xkb_ctx) {
        fprintf(stderr, "Failed to create XKB context\n");
        return false;
      }
      xkb_x11_setup_xkb_extension(conn, 1, 0, 0, NULL, NULL, NULL, NULL);
      xkb_device_id = xkb_x11_get_core_keyboard_device_id(conn);
      if (xkb_device_id == -1) {
        fprintf(stderr, "Failed to get XKB device ID\n");
        return false;
      }
      xkb_keymap =
        xkb_x11_keymap_new_from_device(xkb_ctx, conn, xkb_device_id,
                                       XKB_KEYMAP_COMPILE_NO_FLAGS);
      xkb_state = xkb_x11_state_new_from_device(xkb_keymap, conn,
                                                xkb_device_id);
      screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
      screen_visual = xcb_screen_visual_type(screen);
      xcb_window = xcb_generate_id(conn);
      value_mask = XCB_CW_EVENT_MASK;
      value_list = (u32[]) {XCB_EVENT_MASK_BUTTON_PRESS |
                            XCB_EVENT_MASK_EXPOSURE |
                            XCB_EVENT_MASK_KEY_PRESS |
                            XCB_EVENT_MASK_POINTER_MOTION |
                            XCB_EVENT_MASK_STRUCTURE_NOTIFY};
      xcb_create_window(conn, XCB_COPY_FROM_PARENT, xcb_window,
                        screen->root, window->x, window->y,
                        window->w, window->h, 0,
                        XCB_WINDOW_CLASS_INPUT_OUTPUT,
                        screen->root_visual,
                        value_mask, value_list);
      xcb_change_property(conn, XCB_PROP_MODE_REPLACE, xcb_window,
                          XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
                          strlen(window->title), window->title);
      xcb_map_window(conn, xcb_window);
      pixmap = xcb_generate_id(conn);
      xcb_create_pixmap(conn, screen->root_depth, pixmap, xcb_window,
                        window->w, window->h);
      surface = cairo_xcb_surface_create(conn, pixmap, screen_visual,
                                         window->w, window->h);
      window->cr = cairo_create(surface);
      xcb_flush(conn);
      if (! (r = window->load(window)))
        goto clean;
      while (1) {
        if ((event = xcb_poll_for_event(conn))) {
          if (! (r = window_cairo_xcb_event(window, conn, screen,
                                            screen_visual, &pixmap,
                                            &surface, xcb_window,
                                            event, xkb_state)))
            goto clean;
        }
        else {
          sleep.tv_sec = 0;
          sleep.tv_nsec = 1000000000 / 120;
          nanosleep(&sleep, NULL);
          xcb_expose_event_t event = {0};
          event.response_type = XCB_EXPOSE;
          event.window = xcb_window;
          event.x = 0;
          event.y = 0;
          event.width = window->w;
          event.height = window->h;
          event.count = 0;
          xcb_send_event(conn, false, xcb_window, XCB_EVENT_MASK_EXPOSURE, (const char *)&event);
          xcb_flush(conn);
        }
      }
     clean:
      cairo_surface_destroy(surface);
      cairo_destroy(window->cr);
      xkb_state_unref(xkb_state);
      xkb_keymap_unref(xkb_keymap);
      xkb_context_unref(xkb_ctx);
      xcb_disconnect(conn);
      return r;
    }
    
    xcb_visualtype_t * xcb_screen_visual_type (xcb_screen_t *screen)
    {
      xcb_depth_iterator_t depth_iter;
      xcb_visualtype_iterator_t visual_iter;
      depth_iter = xcb_screen_allowed_depths_iterator(screen);
      while (depth_iter.rem) {
        visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
        while (visual_iter.rem) {
          if (screen->root_visual == visual_iter.data->visual_id) {
            return visual_iter.data;
          }
          xcb_visualtype_next(&visual_iter);
        }
        xcb_depth_next(&depth_iter);
      }
      return NULL;
    }