Wayland and DRM [DRAFT]
Created at 2017-05-14T18:07:20+09:00

Summery

  • [x] Wayland protocol
    • server creates global
    • client check registry and bind server's global into client's scope
    • transparently marchaling global (event, member method call) between server and client
    • be able to register epoll event fd to out-of-box wayland event loop
  • [x] Server KMS
  • [ ] Server EGL (GBM)
    • eglGetDisplay() or eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, , ...)
    • [x] server EGL compositing client images
      • egl_attach: pulls client egl surface into server gl texture
      • texture_point: composite those gl textures
  • [x] Client EGL (Wayland client)
    • eglGetDisplay() or eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_KHR, .. )
    • assumes server has setup EGL with that platform
    • acts on server's wl_drm global (e.g. authentication)
  • [x] How client passes hw-accel buffer to server
    • Shm (passing shared memory fd via sendmsg)
    • EGL texture/image
    • client: drmPrimeHandleToFD (intel_query_iamge)
    • server: drmPrimeFDToHandle (intel_create_image_from_fds))
  • [.] Understand Mesa (GBM, EGL, GL, Intel, Wayland) directory structure

Mesa source structure

- gbm/
  - main/
    - gbm.c, backend.c
      - gbm_create_device, _gbm_create_device
      - struct backend_desc backends[] = { { "gbm_dri.so", &gbm_dri_backend } }
  - backends/dri/gbm_dri.c
    - struct gbm_backend gbm_dri_backend = { .. .create_device = dri_device_create }
    - dri_screen_create_dri2 (this will load i965_dri.so)

- mesa/
  - drivers/dri/i965/intel_screen.c (part of i965_dri.so) (used by both client and server but they could use different extensions)
    - __driDriverGetExtensions_i965, intelInitScreen2

- egl/
  - main/
    - egldrivers.c
      - _eglBuiltInDrivers[] = { { "egl_dri2", _eglBuiltInDriverDRI2 } }
    - eglapi.c (public entry to eglXXX functions)
  - drivers/dri2/
    - egl_dri2.c (_eglBuiltInDriverDRI2)
    - platform_drm.c (NOTE: server use)
      - dri2_initialize_drm, dri2_drm_display_vtbl
    - platform_wayland.c (NOTE: client use)
      - dri2_initialize_wayland_drm, wl_registry_bind( wl_drm_interface ), wl_drm_listener (e.g. drm_handle_device)
      - dri2_wl_display_vtbl
  - wayland/
    - wayland-drm/wayland-drm.{xml,c,h} (NOTE: protocol and server wl_drm global implementation)
      - wl_drm, wl_drm_interface
      - wayland_drm_init (NOTE: server use)
      - wl_drm_create_prime_buffer (generated from .xml to wayland-drm-client-protocol.h)
    - wayland-egl/wayland-egl.c (NOTE: client use (libwayland-egl.so), header is wayland-egl.h from waylnad repo ?)
      - wl_egl_window_create

Server (sway, wlc)

[ Data structure ]
(sway)
struct sway_config *config
struct key_state key_state_array[]
swayc_t root_container
swayc_t *current_focus
log_importance_t v

(wlc, wl)
wlc
'-' wlc_compositor
'-' wlc_interface (a set of callbacks)
'-' wl_display
  '-' wl_event_loop
  '-' global_list
  '-' client_list

wlc_compositor
'-' wlc_backend
'-' wlc_seat
'-' wlc_shell
'-' wl_global compositor
'-* outputs, views, surfaces, subsurfaces, regions

logind
'-' seat
'-' DBusConnection

input
'-' libinput

udev
'-' udev (handle)
'-' udev_monitor
'-' wl_event_source


wlc_output
'-' wlc_output_information
  '-' (mostly copied from drm_surface's kms)
'-' wlc_backend_surface
  '-' drm_surface (as internal)
    '-' drmModeConnector, drmModeEncoder, drmModeCrtc
    '-' drm_fb
      '-' gbm_bo
  '-' drm_fd
  '-' EGLNativeDisplayType ? (does this matter for gbm ??)
  '-' EGLNativeWindowType ?
'-' wlc_context
  '-' wlc_context_api
    '-' swap, ...
  '-' ctx (platform/context/egl.c)
    '-' EGLDisplay
    '-' EGLContext
    '-' EGLSurface
'-' wlc_render
  '-' wlc_render_api
    '-'
  '-' ctx (platform/render/gles2.c)
    '-' ?

wlc_surface
'-* buffer (as wlc_source)
'-2 wlc_surface_state (as pending and commit)
'-' output
'-' view


[ Main path ((*) indicates wayland event loop callback registration) ]
- main =>
  - register_wlc_handlers =>
    - wlc_set_output_created_cb(handle_output_created) =>
      - wlc.interface.output.created = handle_output_created
    - wlc_set_view_created_cb, wlc_set_input_created_cb, etc ..
  - wlc_init =>
    - wlc.display = wl_display_create =>
      - wl_event_loop_create => epoll_create1
    - vt = wlc_logind_init =>
      - sd_pid_get_session, sd_session_get_seat, sd_session_get_vt
      - wlc_dbus_open => dbus_bus_get_private, dbus_bind
      - setup_dbus => ...
      - take_control => dbus_message_new_method_call(... "TakeControl")
    - wlc_tty_init =>
      - open_tty => open
      - setup_tty =>
        - ioctl(fd, KDSETMODE, KD_GRAPHICS) and etc...
    - wlc_fd_init =>
      - (why do we open files in this child ? for safety or something ?)
      - socketpair and fork
      - on child, communicate with parent for opening file while (kill(parent, 0) == 0)
    - (*) wl_signal_add(&wlc.signals.compositor, &compositor_listener)
    - wlc_resources_init => ...
    - wl_display_add_socket_auto =>
      - struct wl_socket *s = wl_socket_alloc
      - wl_socket_init_for_display_name
      - _wl_display_add_socket => bind, listen, and wl_event_loop_add_fd
    - wl_display_init_shm =>
      - wl_global_create(.. wl_shm_interface .. bind_shm) =>
        - struct wl_global *global = malloc
        - wl_list_insert(display->global_list, ...)
        - wl_resource_post_event
    - wlc_udev_init =>
      - udev_new, udev_monitor_new_from_netlink
      - udev_monitor_filter_add_match_subsystem_devtype "drm" and "input"
      - udev_set_event_loop =>
        - (*) wl_event_loop_add_fd(..., udev_monitor_get_fd(udev.monitor), ..., udev_event, ...) =>
          - add_source => epoll_ctl(... EPOLL_CTL_ADD)
      - (*) wl_signal_add(&wlc_system_signals()->activate, &activate_listener)
    - wlc_input_init =>
      - libinput_udev_create_context
      - libinput_udev_assign_seat
      - input_set_event_loop =>
        - (*) wl_event_loop_add_fd(..., libinput_get_fd(input.handle), input_event, ...)
    - wlc_compositor =>
      - (*) wl_signal_add (e.g. compositor->listener.activate, terminate, xwayland, surface, output, focus)
      - wl_global_create(.. wl_compositor_interface, .. wl_compositor_bind)
      - wlc_source (register constructor for wlc managed resource (e.g. wlc_output, wlc_view, wlc_surface, ...))
      - wlc_seat =>
        - wl_global_create(.. wl_seat_interface ..)
      - wlc_shell (weston demo client doesn't use this interface ? intead they use zxdg_shell_v6)
      - wlc_xdg_shell =>
        - wl_global_create(wlc_display(), &zxdg_shell_v6_interface, 1, xdg_shell, xdg_shell_bind)
      - wlc_backend =>
        - wlc_drm =>
          - wlc_fd_open => wlc_logind_open => take_device (is this equivalent to setDrmMaster ??) =>
            - dbus_message_new_method_call "TakeDevice"
            - fcntl F_GETFL
          - drm.device = gbm_create_device (from mesa) =>
            - _gbm_create_device =>
              - struct gbm_backend *backend = load_backend =>  { "gbm_dri.so", &gbm_dri_backend }
              - backend->create_device (i.e. dri_device_create) =>
                - (inheritance: gbm_device > gbm_drm_device > gdb_dri_device)
                - dri->base.base.bo_create = gbm_dri_bo_create
                - dri->base.base.surface_create = gbm_dri_surface_create
                - ...
                - dri_screen_create =>
                  - loader_get_driver_for_fd =>
                    - loader_get_pci_id_for_fd => drm_get_pci_id_for_fd =>
                      - drmGetDevice2 and get vendor_id and chip_id
                    - iterate over driver_map[]
                      - on my pc, it will return "i965"
                        - vendor_id: 0x8086
                        - chip_id: CHIPSET(0x1916, skl_gt2, "Intel(R) HD Graphics 520 (Skylake GT2)") (pci_ids/i965_pci_ids.h)
                  - dri_screen_create_dri2(.. "i965") =>
                    - dri_load_driver =>
                      - dri_open_driver =>
                        - dlopen("... i965_dri.so")
                        - __driDriverGetExtensions_i965 =>
                          - globalDriverAPI = &brw_driver_api
                          - brw_driver_extensions
                      - dri_bind_extensions(.. , gbm_dri_device_extensions, brw_driver_extensions) =>
                        - dri->core = driCoreExtension
                        - dri->dri2 = driDRI2Extension
                    - dri->dri2->createNewScreen2 (i.e. driCreateNewScreen2) =>
                      - __DRIscreen *psp = calloc
                      - psp->driver = globalDriverAPI (i.e. brw_driver_api)
                      - psp->driver->InitScreen (i.e. intelInitScreen2) =>
                        - struct intel_screen *screen = rzalloc
                        - dri_screen->driverPrivate = screen
                        - ?? (video card specific setup ..?)
                    - dri_bind_extensions(.. dri_core_extensions ..) => ...
          - (*) wl_event_loop_add_fd(..., drm.fd, ..., drm_event)
          - backend->api.update_outputs = update_outputs
  - register_extensions =>
    - wl_global_create(.. desktop_shell_interface .. desktop_shell_bin)
    - wl_global_create(.. lock_interface .. swaylock_bind)
  - init_layout =>
    - root_container.id = 0
    - current_focus = &root_container
  - ipc_init =>
    - socket, bind on SWAYSOCK, and listen
    - (*) wlc_event_loop_add_fd(ipc_socket, ..., ipc_handle_connection, ..) => wl_event_loop_add_fd
  - load_main_config =>
    - input_init
    - load_config
    - update_active_bar_modifiers
  - wlc_run =>
    - wlc_set_actives => wl_signal_emit(&wlc.signals.activate, ...) => (SEE BELOW, for possible callbacks)
    - wl_display_run =>
      - while (display->run)
        - wl_display_flush_clients => ...
        - wl_event_loop_dispatch =>
          - epoll_wait
          - struct wl_event_source *source = ep[i].data.ptr
          - source->interface->dispatch => ...(one of callbacks)


[ on activate (kms routine) ]
- activate_event (compositor.c) =>
  - activate_tty => wlc_fd_activate => wlc_tty_activate => ioctl(wlc.tty, VT_RELDISP...)
  - wlc_backend_update_outputs => update_outputs (drm.c) =>
    - query_drm (kms-based device discovery) =>
      - drmModeGetResources, drmModeGetConnector
      - find_encoder_for_connector =>
      - find_crtc_for_encoder =>
      - wlc_output_information
      - wlc_output_information_add_mode
    - gbm_surface_create(... GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING) =>
      - gbm->surface_create (i.e. gbm_dri_surface_create) =>
        - struct gbm_dri_surface *surf = calloc
    - add_output(... ) =>
      - wlc_backend_surface => ...
      - bsurface.drm_fd = drm.fd;
      - bsurface.display = (EGLNativeDisplayType)device
      - bsurface.display_type = EGL_PLATFORM_GBM_KHR
      - bsurface.window = (EGLNativeWindowType)surface (casted from struct gbm_surface)
      - wl_signal_emit(&wlc_system_signals()->output, &ev) (WLC_OUTPUT_EVENT_ADD) => (TO COMPOSITOR, SEE BELOW)

- activate_event (session/udev.c) =>
  - libinput_resume => ...


[ output initialization (egl/gl context setup for compositor itself) ]
- output_event (compositor.c) =>
  - (for WLC_OUTPUT_EVENT_ADD) add_output =>
    - struct wlc_output *output = wlc_handle_create(&compositor->outputs) =>
      - handle_create => constructor (i.e. wlc_output) =>
        - output->timer.idle = wl_event_loop_add_timer(... cb_idle_timer)
        - wl_global_create(... &wl_output_interface ... wl_output_bind))
    - wlc_output_set_information =>
      - wlc_output_set_resolution_ptr =>
        - WLC_INTERFACE_EMIT(output.resolution, ...) => (sway) handle_output_resolution_change
        - wlc_output_schedule_repaint
          - wl_event_source_timer_update(output->timer.idle, 1) => timerfd_settime
    - wlc_output_set_backend_surface =>
      - wlc_context(&output->context, &output->bsurface) =>
        - wlc_egl =>
          - create_context =>
            - get_display =>
              - eglGetDisplay (or eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, <gdb_device> ...)) =>
                - (NOTE that the "platform" is different from the one the client does (SEE BELOW simple-egl))
                - _eglGetNativePlatform => _eglNativePlatformDetectNativeDisplay => _EGL_PLATFORM_DRM
                - _eglFindDisplay => _EGLDisplay *dpy = calloc
            - eglInitialize =>
              - _eglMatchDriver => _eglMatchAndInitialize =>
                - _eglAddDrivers => _eglAddBuiltInDrivers =>
                  - go thgourh _eglBuiltInDrivers (i.e. { "egl_dri2", _eglBuiltInDriverDRI2 })
                - _eglLoadModule => _eglBuiltInDriverDRI2 =>
                  - dri2_drv->base.API.Initialize = dri2_initialize
                  - dri2_drv->base.API.BindWaylandDisplayWL = dri2_bind_wayland_display_wl (? is this server only call ??)
                  - dri2_drv->base.API.SwapBuffers = dri2_swap_buffers
                  - ...
                - dri2_initialize =>
                  - (for client case, it goes to dri2_initialize_wayland)
                  - dri2_initialize_drm =>
                    - struct dri2_egl_display *dri2_dpy = calloc
                    - dri2_dpy->gbm_dri = gbm_dri_device(<gbm_device>)
                    - dri2_dpy->core = dri2_dpy->gbm_dri->core, dri2, ... (coping method from gbm_device)
                    - dri2_setup_screen => mark available extension (e.g. _egl_display.Extensions.KHR_fence_sync)
                    - dri2_set_WL_bind_wayland_display => _egl_display.Extensions.WL_bind_wayland_display
                    - dri2_dpy->vtbl = dri2_drm_display_vtbl
            - eglBindAPI(EGL_OPENGL_ES_API)
            - eglCreateContext => dri2_create_context => (same to client case)
            - create_surface_gbm => eglCreateWindowSurface => dri2_create_window_surface =>
              - dri2_drm_create_window_surface => dri2_drm_create_surface(.. EGL_WINDOW_BIT) =>
                - struct dri2_egl_surface *dri2_surf = calloc
                - dri2_surf->gbm_surf = <gbm_dri_surface>
                - dri2_surf->dri_drawable = driCreateNewDrawable (as dri2_dpy->dri2->createNewDrawable) =>
                  - intelCreateBuffer (as screen->driver->CreateBuffer) => (same to client case)
            - eglMakeCurrent
            - eglSwapInterval(.. 1)
          - api->swap = swap ...
      - wlc_context_bind_to_wl_display =>
        - bind_to_wl_display (egl.c) => eglBindWaylandDisplayWL =>
          - dri2_bind_wayland_display_wl =>
            - dri2_dpy->wl_server_drm = wayland_drm_init(<wayland_drm_callbacks>) =>
              - wl_global_create(display, &wl_drm_interface, 2, drm, bind_drm)
      - wlc_render =>
        - wlc_context_bind => bind (egl.c) => eglMakeCurrent
        - wlc_gles2 =>
          - create_context =>
            - gl calls ...
      - WLC_INTERFACE_EMIT(output.context.created ..) => (no sway callback)
      - wl_signal_emit(&wlc_system_signals()->output) with WLC_OUTPUT_EVENT_SURFACE => who watches ?
    - WLC_INTERFACE_EMIT_EXCEPT(output.created, ...) => call sway's callback
    - active_output =>
      - WLC_INTERFACE_EMIT(output.focus, ..) => ...
      - wlc_output_schedule_repaint

- handle_output_created (sway) =>
  - swayc_t *op = new_output
  - wlc_output_set_mask(output, VISIBLE)
  - swayc_active_workspace

[ drm event ]
- drm_event =>
  - drmEventContext evctx
  - evctx.page_flip_handler = page_flip_handler
  - drmHandleEvent =>
    - (case DRM_EVENT_FLIP_COMPLETE) page_flip_handler =>
      - release_fb(dsurface->gbm_surface ..) =>
        - drmModeRmFB(drm.fd, fb->fd)
        - gbm_surface_release_buffer(surface, fb->bo) => ...
      - wlc_output_finish_frame =>
        - there's some task associated to output ? ...
      - dsurface->flipping = false

[ output rendering ]
- cb_idle_timer =>
  - repaint =>
    - should_render
    - wlc_render_resolution => resolution (gles2.c) => ...
    - WLC_INTERFACE_EMIT(output.render.pre ..) => (sway) handle_view_pre_render => render_view_borders ...
    - render_view for each output->visible =>
      - WLC_INTERFACE_EMIT(view.render.pre ..)
      - wlc_render_view_paint => view_paint (gles2.c) =>
        - wlc_view_get_bounds(view, &geometry, &settings.visible)
        - surface_paint_internal => texture_paint(..  surface->textures, <wlc_geometry>) =>
          - glActiveTexture(GL_TEXTURE0 + i) and glBindTexture(GL_TEXTURE_2D, textures[i])
          - glVertexAttribPointer(... vertices) (vertices are define from wlc_geometry (2d position and size))
          - glVertexAttribPointer(... coords)
          - glDrawArrays
      - subsurfaces_render => ...
      - wlc_render_flush_fakefb
    - wlc_context_swap(&output->context, &output->bsurface) =>
      - swap (egl.c) =>
        - bind => eglMakeCurrent
        - eglSwapBuffers => drv->API.SwapBuffers (i.e. dri2_swap_buffers) =>
          - dri2_drm_swap_buffers (as dri2_dpy->vtbl->swap_buffers) =>
            - dri2_surf->current = dri2_surf->back
            - dri2_flush_drawable_for_swapbuffers =>
              - intel_dri2_flush_with_flags (as dri2_dpy->flush->flush_with_flags) => ...
        - page_flip (drm.c) =>
          - create_gbm_fb(<gbm_surface>, <drm_fb>) =>
            - fb->bo = gbm_surface_lock_front_buffer(<gbm_surface>) =>
              - lock_front_buffer (from platform_drm.c) (as surf->gbm->surface_lock_front_buffer) =>
                - struct gbm_bo *bo = dri2_surf->current->bo
                - dri2_surf->current->locked = 1
            - drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fd)
          - drmModeSetCrtc(drm.fd, .. fb->fd ..)
          - drmModePageFlip(drm.fd, .. fb->fd .. <gbm_device>) => TODO: follow driver
        - (later on page_flip_handler from drm_event) =>
          - release_fb => drmModeRmFB and gbm_surface_release_buffer => release_buffer (from platform_drm.c) =>
            - dri2_surf->current->locked = 0

[ rensponds to client actions via global ]
(wl_compositor::create_surface)
- wl_cb_surface_create (as wl_compositor_implementation.create_surface) =>
  - wlc_resource_create(.. wl_surface_interface)
  - wlc_surface_implementation =>
    - attach = wl_cb_surface_attach
    - frame = wl_cb_surface_frame
    - commit = wl_cb_surface_commit
    - damage = wl_cb_surface_damage
    - ...
  - wl_signal_emit(&wlc_system_signals()->surface, &ev) with WLC_SURFACE_EVENT_CREATED =>
    - surface_event (compositor.c) => no action for WLC_SURFACE_EVENT_CREATED

(wl_shm::create_pool)
- ??

(wl_shm_pool::create_buffer)
- ??

(wl_drm::create_prime_buffer)
- drm_create_prime_buffer
  - create_buffer =>
    - struct wl_drm_buffer *buffer = calloc
    - drm->callbacks->reference_buffer(.. fd ) (i.e. dri2_wl_reference_buffer) =>
      - img = dri2_dpy->image->createImageFromFds (i.e. intel_create_image_from_fds) =>
        - __DRIimage *img = intel_allocate_image
        - image->bo = brw_bo_gem_create_from_prime(.. fds[0] ..) => drmPrimeFDToHandle
      - buffer->driver_buffer = img
    - wl_resource_create(.. wl_buffer_interface)

(wl_drm::authenticate)
- drm_authenticate =>
  - drm->callbacks->authenticate (i.e. dri2_drm_authenticate) =>
    - drmAuthMagic(dri2_dpy->fd, id)
  - wl_resource_post_event(resource, WL_DRM_AUTHENTICATED)

(wl_surface::attach)
- wl_cb_surface_attach (as wl_surface_implementation.attach) =>
  - associate wlc_surface with buffer (both underlying data is wl_resource)

(wl_surface::damage)
- wl_cb_surface_damage =>
  - pixman_region32_union_rect

(wl_surface::commit)
- wl_cb_surface_commit =>
  - commit_subsurface_state =>
    - commit_state =>
      - pixman_region32_xxx ...
      - surface_attach =>
        - wlc_surface_attach_to_output =>
          - wlc_output_surface_attach =>
            - wlc_render_surface_attach =>
              - render->api.surface_attach (egl.c surface_attach) =>
                - (if shm_buffer) shm_attach => ??
                - (otherwise (i.e. wayland egl buffer))
                  - wlc_context_query_buffer(.. EGL_TEXTURE_FORMAT) => eglQueryWaylandBufferWL
                  - egl_attach =>
                    - surface->images[i] = wlc_context_create_image(.. EGL_WAYLAND_BUFFER_WL) =>
                      - context->api.create_image => context->api.eglCreateImageKHR =>
                        - dri2_create_image  (as drv->API.CreateImageKHR) =>
                          - dri2_drm_create_image_khr (as dri2_dpy->vtbl->create_image) =>
                            - dri2_create_image_khr => dri2_create_image_wayland_wl_buffer =>
                              - struct wl_drm_buffer *buffer = wayland_drm_buffer_get
                              - __DRIimage *dri_image =  = dri2_dpy->image->fromPlanar(buffer->driver_buffer ..) =>
                                - intel_from_planar => ...
                              - dri2_create_image_from_dri (from __DRIImage to _EGLImage) =>
                                - struct dri2_egl_image *dri2_img = malloc
                                - dri2_img->dri_image = dri_image
                    - glActiveTexture(GL_TEXTURE0 + i) and glBindTexture(GL_TEXTURE_2D, surface->textures[i])
                    - context->api.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, surface->images[i]) =>
                      - _mesa_EGLImageTargetTexture2DOES =>
                        - ctx->Driver.EGLImageTargetTexture2D(.. <gl_texture_image> <GLeglImageOES> ..) =>
                          - intel_image_target_texture_2d =>
                            - __DRIimage *image = dri_screen->dri2.image->lookupEGLImage( <GLeglImageOES> )
                            - intel_set_texture_image_mt(<gl_texture_image>, image) => ...
        - wlc_view_map => ?
        - wlc_view_ack_surface_attach => ?
    - wlc_output_schedule_repaint
    - (recursively) commit_subsurface_state

(zxdg_shell_v6::get_xdg_surface)
(see more wayland-protocol/unstable/xdg-shell/xdg-shell-unstable-v6.xml)
- xdg_cb_shell_get_surface (as zxdg_shell_v6_interface.get_xdg_surface) =>
  - wlc_resource_create(.. zxdg_surface_v6_interface ..)
  - wlc_resource_implement(.. zxdg_surface_v6_implementation ..)

(zxdg_surface_v6::get_toplevel)
- xdg_cb_surface_get_toplevel (as zxdg_surface_v6_interface.get_toplevel) =>
  - wlc_resource_create(.. zxdg_toplevel_v6_interface ..)
  - wlc_resource_implement(.. wlc_xdg_toplevel_implementation ..)
  - wl_signal_emit(&wlc_system_signals()->surface, &ev) (type is WLC_SURFACE_EVENT_REQUEST_VIEW_ATTACH)
    - (callback on wlc compositor) surface_event =>
      - attach_surface_to_view_or_create =>
        - struct wlc_view *view = wlc_compositor_view_for_surface =>
          - wlc_handle_create(&compositor->views)
          - wlc_surface_attach_to_output
          - wlc_surface_attach_to_view
  - zxdg_surface_v6_send_configure(resource, wl_display_next_serial(wlc_display())) => post something to client ??


[ global events to client ]
- bind_drm =>
  - wl_resource_create(client, &wl_drm_interface, ..)
  - wl_resource_post_event(resource, WL_DRM_DEVICE, drm->device_name)
  - wl_resource_post_event(resource, WL_DRM_CAPABILITIES, capabilities)

[ libinput event ]
??

[ drm event ]
??

[ Logging facility ]
(sway)
typedef enum {
    L_SILENT = 0,
    L_ERROR = 1,
    L_INFO = 2,
    L_DEBUG = 3,
} log_importance_t;
- main =>
  - wlc_log_set_handler(wlc_log_handler) => wlc.log_fun = ...
- sway_log => _sway_log => _sway_vlog => chekc if (verbosity <= v) ...

(wlc)
enum wlc_log_type {
   WLC_LOG_INFO,
   WLC_LOG_WARN,
   WLC_LOG_ERROR,
   WLC_LOG_WAYLAND,
};
- wlc_init =>
  - wl_log_set_handler_server(wl_cb_log) => wl_log_handler = ...
- wlc_log(type, ) => wlc_vlog(type, ) => wlc.log_fun (i.e. wlc_log_handler from sway) => sway_log
- wlc_dlog => wlc_vlog(WLC_LOG_INFO, ) => ...

(wl)
- wl_log => wl_log_handler (i.e. wl_cb_log) =>
  - wlc_vlog(WLC_LOG_WAYLAND, ...)

Client shared memory

weston-eventdemo --no-border

(main path)
- main =>
  - struct display *d = display_create =>
    - wl_display_connect =>
      - connect_to_socket =>
        - wl_os_socket_cloexec =>
        - concat getenv("XDG_RUNTIME_DIR") and getenv("WAYLAND_DISPLAY") to get domain socket
        - connect
      - wl_display_connect_to_fd =>
        - wl_event_queue_init default_queue and display_queue
        - wl_connection_create
    - os_epoll_create_cloexec => epoll_create
    - d->display_task.run = handle_display_data
    - (*) display_watch_fd(..., EPOLLIN | EPOLLERR | EPOLLHUP, &d->display_task) =>
      - epoll_ctl
    - wl_display_get_registry
    - (*) wl_registry_add_listener(... registry_listener ...)
    - wl_display_roundtrip (this will ensure basic global setup (e.g. compositor) via registry_listener) =>
      - wl_display_roundtrip_queue => wl_display_dispatch_queue ...
    - create_cursors => ...
    - init_dummy_surface => ...
  - eventdemo_create =>
    - window_create =>
      - window_create_internal =>
        - surface_create =>
          - wl_compositor_create_surface (generated from protocol xml) =>
            - wl_proxy_marshal_constructor(... WL_COMPOSITOR_CREATE_SURFACE, wl_surface_interface ...) =>
              - wl_proxy_marshal_array_constructor => wl_proxy_marshal_array_constructor_versioned =>
                - struct wl_closure *closure = wl_closure_marshal
                - if (debug_client) wl_closure_print
                - wl_closure_send
          - (*) wl_surface_add_listener(... surface_listener)
        - window->main_surface = surface
        - surface->buffer_type = get_preferred_buffer_type => WINDOW_BUFFER_TYPE_SHM (cairo without GL)
      - xdg_surface = zxdg_shell_v6_get_xdg_surface
      - zxdg_surface_v6_add_listener(... xdg_surface_listener)
      - xdg_toplevel = zxdg_surface_v6_get_toplevel
      - zxdg_toplevel_v6_add_listener(... xdg_toplevel_listener)
      - wl_surface_commit
    - window_add_widget => widget_create =>
      - widget->allocation = surface->allocation
      - widget.use_cairo = 1
    - widget_set_redraw_handler(e->widget, redraw_handler)
    - window_set_key_handler
    - widget_set_resize_handler
    - window_schedule_resize =>
      - window_schedule_redraw =>
        - window_schedule_redraw_task =>
          - window->redraw_task.run = idle_redraw
          - display_defer(.. window->redraw_task) => wl_list_insert(... deferred_list)
  - display_run =>
    - while 1
      - run task from deferred_list (e.g. window redraw_task)
      - wl_display_dispatch_pending
      - wl_display_flush
      - if wl_display_flush didn't flush enough, epoll_ctl to include EPOLLOUT for display connection fd
      - epoll_wait
      - task->run (e.g. handle_display_data) ...


(epoll on display connection fd)
- handle_display_data =>
  - (EPOLLOUT)
    - wl_display_flush
    - epoll_ctl to exclude EPOLLOUT for display connection fd
  - (EPOLLIN)
    - wl_display_dispatch =>
      - wl_display_dispatch_queue(..., default_queue) =>
        - wl_display_read_events => read_events =>
          - wl_connection_read and queue_event ...
        - wl_display_dispatch_queue_pending => dispatch_queue => dispatch_event =>
          - struct wl_closure *closure = container_of(queue->event_list.next, ...)
          - if debug_client (i.e. WAYLAND_DEBUG=client) wl_closure_queue
          - wl_closure_dispatch ...


(bind wayland server's interface on client)
- registry_handle_global (as wl_registry_listener.global event) =>
  - (wl_compositor)
    - wl_registry_bind(... wl_compositor_interface ...)
  - (wl_seat)
    - display_add_input =>
      - wl_registry_bind(.. wl_seat_interface ..)
      - wl_seat_add_listener(.. seat_listener ..)
      - pointer_surface = wl_compositor_create_surface
      - ... (setup cursor timer task and keyboard repeat timer task)
  - (zxdg_shell_v6)
    - d->xdg_shell = wl_registry_bind(registry, id, &zxdg_shell_v6_interface, 1)
    - zxdg_shell_v6_add_listener(d->xdg_shell, &xdg_shell_listener, d)
  - (shm)
    - wl_registry_bind(registry, id, &wl_shm_interface, 1);
        - wl_shm_add_listener(d->shm, &shm_listener, d);



(seat (input) setup)
- seat_handle_capabilities (as wl_seat_listener.capabilities event) =>
  - keyboard = wl_seat_get_keyboard(seat)
  - wl_keyboard_add_listener(keyboard, &keyboard_listener ..)
  - ... pointer setup too


(key handler)
- keyboard_handle_key (as wl_keyboard_listener.key) =>
  - window->key_handler =>
    - printf("key key:  ...)


(surface creation)
- surface_create_surface =>
  - surface->toysurface = shm_surface_create =>
    - struct shm_surface
    - surface->base.prepare = shm_surface_prepare
    - surface->base.swap = shm_surface_swap
  - cairo_surface = surface->toysurface->prepare (i.e. shm_surface_prepare) =>
    - display_create_shm_surface =>
      - shm_pool_create => make_shm_pool =>
        - os_create_anonymous_file => create_tmpfile_cloexec => mkostemp
        - mmap(..  PROT_READ | PROT_WRITE, MAP_SHARED)
        - wl_shm_create_pool (pass fd to server with sendmsg)
      - display_create_shm_surface_from_pool =>
        - void *map = shm_pool_allocate(.. &offset) (cut out a part of shared memory for this cairo rendering space)
        - cairo_surface_t * = cairo_image_surface_create_for_data(map, ...)
        - wl_shm_pool_create_buffer(.. offset)
        - return surface
    - wl_buffer_add_listener(.. shm_surface_buffer_listener)


(redraw (after resize if needed))
- idle_redraw =>
  - idle_resize =>
    - window_do_resize =>
      - widget_set_allocation => widget->allocation.x = ...
      - surface_resize =>
        - widget->resize_handler =>
          - resize_handler (provided by app) =>
            - widget_set_size => widget->allocation.width = ...
        - surface->allocation = widget->allocation
  - surface_redraw =>
    - wl_surface_frame and wl_callback_add_listener (frame_callback)
    - widget_get_cairo_surface =>
      - window_create_main_surface => surface_create_surface ...
    - widget_redraw =>
      - widget->redraw_handler (provided by app, SEE BELOW)
  - window_flush =>
    - surface_flush =>
      - surface->toysurface->swap (i.e. shm_surface_swap) =>
        - wl_surface_attach
        - wl_surface_damage
        - wl_surface_commit

- redraw_handler =>
  - struct rectangle rect
  - widget_get_allocation
  - cairo_surface_t *surface = window_get_surface
  - cairo_t *cr = cairo_create(surface)
  - cairo calls ...

- frame_callback (wl_callback_listener.done) =>
  - wl_callback_destroy
  - window_schedule_redraw_task again if not redraw_task_scheduled


(passing file descriptor (more info on unix(7), sendmsg(2), csmg(3))
- wl_closure_send =>
  - copy_fds_to_connection =>
    - wl_connection_put_fd
    - wl_connection_write =>
      - wl_connection_flush =>
        - build_cmsg =>
              - cmsg->cmsg_level = SOL_SOCKET;
              - cmsg->cmsg_type = SCM_RIGHTS;
              - cmsg->cmsg_len = CMSG_LEN(size);
        - sendmsg ...


[ Data structure ]
display
'-' wl_display
  '-' wl_proxy
  '-' wl_connection
  '-' fd
  '-' wl_event_queue (display_queue);
    '-' wl_event_queue (default_queue);
'-' wl_registry
'-' wl_compositor
'-* window
  '-' window_key_handler_t, ...
  '-' redraw_task
  '-' surface (as main_surface)
    '-' wl_surface
    '-' wl_region
    '-' toysurface
      '-' cairo_surface_t *(*prepare)(...)
      '-' void (*swap)(...)
    '-' widget
      '-' widget_redraw_handler_t, widget_button_handler_t, ...

shm_surface
'-' toysurface (inherit to )

Client EGL

weston-simple-egl

- main =>
  - wl_display_connect, wl_display_get_registry, wl_registry_add_listener, wl_display_roundtrip
  - init_egl =>
    - weston_platform_get_egl_display =>
      - eglGetDisplay (or eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_KHR, <wl_display> .. )) =>
        - _eglGetNativePlatform =>
          - _eglNativePlatformDetectNativeDisplay => _EGL_PLATFORM_WAYLAND (tricky pointer equivalence)
        - _eglFindDisplay => calloc _EGLDisplay
        - _eglGetDisplayHandle => cast from _EGLDisplay to EGLDisplay
    - eglInitialize =>
      - _eglMatchDriver => _eglMatchAndInitialize =>
        - _eglAddDrivers => _eglAddBuiltInDrivers =>
          - go through _eglBuiltInDrivers (i.e. { "egl_dri2", _eglBuiltInDriverDRI2 })
        - _eglLoadModule => _eglBuiltInDriverDRI2 =>
          - dri2_load => dlopen("libglapi.so.0") and dlsym(.. "_glapi_get_proc_address")
          - dri2_drv->base.API.Initialize = dri2_initialize
          - dri2_drv->base.API.BindWaylandDisplayWL = dri2_bind_wayland_display_wl (? is this server only call ??)
          - dri2_drv->base.API.SwapBuffers = dri2_swap_buffers
          - ...
        - dri2_initialize =>
          - dri2_initialize_wayland =>
            - dri2_initialize_wayland_drm =>
              - struct dri2_egl_display *dri2_dpy = calloc
              - wl_display_create_queue
              - wl_proxy_create_wrapper
              - wl_display_get_registry, wl_registry_add_listener(registry_listener_drm)
              - roundtrip (this will force server to give information about global wl_drm on registry)
                - registry_listener_drm.global (aka registry_handle_global_drm) =>
                  - dri2_dpy->wl_drm = wl_registry_bind(.. wl_drm_interface)
                  - wl_drm_add_listener(.. drm_listener)
              - roundtrip (this will force server to bind_drm, which post_event WL_DRM_DEVICE)
                - drm_handle_device (as wl_drm_listener.device) =>
                  - dri2_dpy->device_name = strdup(device)
                  - dri2_dpy->fd = loader_open_device(dri2_dpy->device_name)
                  - drmGetMagic(dri2_dpy->fd, &magic)
                  - wl_drm_authenticate(dri2_dpy->wl_drm, magic) (SEE ABOVE for how server reacts)
              - roundtrip (this will force server to reacts to previous wl_drm_authenticate, which hopefully post_event WL_DRM_AUTHENTICATED)
                - drm_handle_authenticated => dri2_dpy->authenticated = 1
              - dri2_dpy->driver_name = loader_get_driver_for_fd =>
                - (same as server, you will get 'i965'. SEE ABOVE)
              - dri2_load_driver =>
                - (similar to server's dri_load_driver, but this could take in different extension)
                - dri2_open_driver => dlopen i965_dri.so
                - dri2_bind_extensions(dri2_dpy, dri2_driver_extensions, extensions, false) =>
                  - dri2_dpy->core = driCoreExtension
                  - dri2_dpy->dri2 = driDRI2Extension
              - dri2_create_screen =>
                - dri2_dpy->dri_screen = dri2_dpy->dri2->createNewScreen2 (i.e. driCreateNewScreen2) =>
                  - (same as server)...
                  - __DRIscreen *psp = calloc
                  - psp->driver = globalDriverAPI (i.e. brw_driver_api)
                  - psp->driver->InitScreen (i.e. intelInitScreen2) =>
                    - dri_screen->extensions = { intelImageExtension ... }
                - dri2_bind_extensions(dri2_dpy, dri2_core_extensions, { intelImageExtension } ..) =>
                  - dri2_dpy->image = intelImageExtension
              - dri2_set_WL_bind_wayland_display
              - dri2_dpy->vtbl = dri2_wl_display_vtbl
    - eglBindAPI(EGL_OPENGL_ES_API)
    - eglCreateContext => dri2_create_context  (as drv->API.CreateContext) =>
      - struct dri2_egl_context *dri2_ctx = malloc
      - _eglInitContext => ...
      - dri2_ctx->dri_context = dri2_dpy->dri2->createContextAttribs (i.e. driCreateContextAttribs) =>
        - screen->driver->CreateContext (i.e. brwCreateContext) =>
          - struct brw_context *brw = rzalloc
          - brw_init_driver_functions =>
            - _mesa_init_driver_functions (setup default dd_function_table ?)
            - functions->Flush = intel_glFlush
            - functions->Finish = intel_finish
            - ...
          - _mesa_initialize_context(<gl_context> <gl_api> <dd_function_table>..) =>
            - ctx->API = api
            - ctx->Driver = *driverFunctions
            - ctx->OutsideBeginEnd = _mesa_alloc_dispatch_table =>
              - struct _glapi_table *table = _mesa_new_nop_table
            - ctx->Exec = ctx->OutsideBeginEnd
          - _vbo_CreateContext => struct vbo_context *vbo = CALLOC_STRUCT
          - _mesa_initialize_dispatch_tables => _mesa_initialize_exec_table (api_exec.c is dynamically generated on build) =>
            - vbo_initialize_exec_dispatch =>
            - a bunch of SET_XXX(exec, _mesa_XXX) (e.g. SET_VertexAttribPointer(exec, _mesa_VertexAttribPointer))
          - and much more setup ...??
  - create_surface =>
    - wl_compositor_create_surface
    - wl_egl_window_create => malloc struct wl_egl_window
    - weston_platform_create_egl_surface =>
      - eglCreateWindowSurface =>
        - _eglCreateWindowSurfaceCommon => dri2_create_window_surface (as API.CreateWindowSurface) =>
          - dri2_wl_create_window_surface (as dri2_dpy->vtbl->create_window_surface) =>
            - struct dri2_egl_surface *dri2_surf = calloc
            - dri2_surf->dri_drawable = dri2_dpy->dri2->createNewDrawable (i.e. driCreateNewDrawable) =>
              - __DRIdrawable *pdraw = malloc
              - screen->driver->CreateBuffer (i.e. intelCreateBuffer) =>
                - struct gl_framebuffer *fb = CALLOC_STRUCT and _mesa_initialize_window_framebuffer
                - struct intel_renderbuffer *rb = intel_create_renderbuffer => ??
                - _mesa_attach_and_own_rb => ...
                - _swrast_add_soft_renderbuffers
                - driDrawPriv->driverPrivate = fb
    - create_xdg_surface =>
      - zxdg_shell_v6_get_xdg_surface
      - wl_surface_commit
    - eglMakeCurrent(<EGLDisplay>, <EGLSurface>x2, <EGLContext>) => drv->API.MakeCurrent (i.e. dri2_make_current) =>
      - _eglBindContext => ??
      - dri2_dpy->core->bindContext (i.e. driBindContext) =>
        - pcp->driScreenPriv->driver->MakeCurrent (i.e. intelMakeCurrent from brw_driver_api) =>
          - _mesa_make_current => ??
    - eglSwapInterval => ??
  - init_gl => gl calls (shader setup)
  - redraw =>
    - gl calls ... glDrawArrays
    - eglSwapBuffers => drv->API.SwapBuffers (i.e. dri2_swap_buffers) =>
      - dri2_wl_swap_buffers (as dri2_dpy->vtbl->swap_buffers) => dri2_wl_swap_buffers_with_damage =>
        - create_wl_buffer =>
          - dri2_dpy->image->queryImage(dri2_surf->current->dri_image, __DRI_IMAGE_ATTRIB_FD, &fd) =>
            - intel_query_image => (for __DRI_IMAGE_ATTRIB_FD) brw_bo_gem_export_to_prime => drmPrimeHandleToFD
          - dri2_surf->current->wl_buffer = wl_drm_create_prime_buffer(.. fd) (SEE ABOVE for how server handles this)
          - wl_buffer_add_listener(dri2_surf->current->wl_buffer, &wl_buffer_listener, dri2_surf)
            (server will tell client when buffer is released)
        - wl_surface_attach, wl_surface_damage, wl_surface_commit, wl_display_flush (SEE ABOVE for how server handles these)


[ Data structure ]
_egl_display (aka _EGLDisplay or EGLDisplay)
'-' mtx_t
'-' _EGLPlatformType (e.g. _EGL_PLATFORM_WAYLAND)
'-' PlatformDisplay (e.g. wl_display)
'-' _EGLDriver (aka _egl_driver)
  '-' _EGLAPI (aka _egl_api)
    '-' Initialize, CreateContext, CreateWindowSurface
'-' DriverData (e.g. dri2_egl_display)

dri2_egl_display
'-' dri2_egl_display_vtbl (e.g. dri2_wl_display_vtbl)
  '-' authenticate, create_window_surface, swap_buffers, ...
'-' wl_display
'-' wl_event_queue
'-' __DRIdri2Extension (and other "extensions")
  '-' createNewDrawable, createNewScreen2
'-' __DRIscreen
  '-' __DriverAPIRec (e.g. brw_driver_api)
    '-' InitScreen, CreateContext, CreateBuffer, ...

dri2_egl_context (< _EGLContext)
'-' _EGLContext (holds some configs ? e.g. )
  '-2 _EGLSurface (DrawSurface and ReadSurface)
'-' __DRIcontext
  '-' driverPrivate (e.g. brw_context)

brw_context (< gl_context)
'-' gl_context
  '-' gl_api (enum e.g. API_OPENGLES2)
  '-' _glapi_table (holds gl and other)
  '-' dd_function_table (holds glFlush and others)
  '-* gl_framebuffer (DrawBuffer, ReadBuffer and other several)
'-' ... (intel or dri specific things ??)

dri2_egl_surface (< _EGLSurface)
'-' __DRIdrawable
  '-' gl_framebuffer (as driverPrivate)
    '-* gl_renderbuffer_attachment
      '-' intel_renderbuffer (< swrast_renderbuffer < gl_renderbuffer)
'-' wl_egl_window
  '-' wl_surface
  '-' dri2_egl_surface (back pointer as private)
'-2 { wl_buffer, __DRIimage, .. } (as current and back)

EGL dispatching indirection (e.g. eglSwapBuffers)

[ data structure ]

dri2_egl_driver < EGLDriver (setup via _eglBuiltInDriverDRI2 (which in turn from eglInitialize))
'-' _EGLAPI
  '-' SwapBuffers

EGLDisplay (setup via dri2_initialize_<some-platform> (e.g. dri2_initialize_wayland_drm))
'-' dri2_egl_display (as DriverData)
  '-' dri2_egl_display_vtbl
    '-' swap_buffers
  '-' __DRIcoreExtension, __DRIdri2Extension, __DRIswrastExtension
      (setup via dri2_bind_extensions within dri2_load_driver)
    '-' swapBuffers

__DRIscreen (setup via driCreateNewScreen2 within dri2_create_screen)
'-' __DriverAPIRec
  '-' SwapBuffers


[ Call chains ]

eglSwapBuffers (egl/main/eglapi.c) =>
_EGLDriver.API.SwapBuffers (e.g. dri2_swap_buffers (egl/drivers/dri2/egl_dri2.c)) =>
dri2_egl_display.vtbl.swap_buffers (e.g. dri2_wl_swap_buffers (egl/drivers/dri2/platform_wayland.c)) =>
... then something platform specific

for software case, it further chains via common infrastructure:

dri2_egl_display.vtbl.swap_buffers (e.g. dri2_wl_swrast_swap_buffers (egl/drivers/dri2/platform_wayland.c)) =>
__DRIcoreExtension.swapBuffers (e.g. driSwapBuffers (mesa/drivers/dri/common/dri_util.c)) =>
__DriverAPIRec.SwapBuffers (e.g. dri_swap_buffers (mesa/drivers/dri/swrast/swrast.c))

Process tree

└─sway─┬─Xwayland─┬─{llvmpipe-0}
       │          ├─{llvmpipe-1}
       │          ├─{llvmpipe-2}
       │          └─{llvmpipe-3}
       ├─sway
       ├─swaybar──...
       └─swaybg

capability setup:

sudo setcap "cap_sys_ptrace+eip cap_sys_tty_config+eip" out/example/example

card on my pc

$ lspci -mm -nn -v -s 00:02.0
Slot:    00:02.0
Class:    VGA compatible controller [0300]
Vendor:    Intel Corporation [8086]
Device:    HD Graphics 520 [1916]
SVendor:    Lenovo [17aa]
SDevice:    Device [5059]
Rev:    07

Misc

Reference