2023-04-13 00:27:08 +03:00
# include "cairo.h"
2023-04-13 04:36:53 +03:00
# include "rect.h"
2023-04-13 00:27:08 +03:00
# include "timeutil.h"
# include "window.h"
# include "node.h"
2023-04-13 18:34:16 +03:00
# include "defs.h"
2023-04-15 21:29:10 +03:00
# include <xkbcommon/xkbcommon.h>
2023-04-13 18:34:16 +03:00
# ifdef _UI_DEBUG
2023-04-13 04:57:54 +03:00
# define PROF
2023-04-13 18:34:16 +03:00
# endif
2023-04-13 00:27:08 +03:00
# include "prof.c"
# include "time.h"
2023-04-13 04:11:15 +03:00
# include <stdint.h>
2023-04-13 00:27:08 +03:00
# include <stdlib.h>
# include <sys/poll.h>
# include <time.h>
# include <xcb/xcb.h>
2023-04-13 04:36:53 +03:00
# include <xcb/xproto.h>
2023-04-15 21:29:10 +03:00
# include <xcb/xkb.h>
# include <xkbcommon/xkbcommon-x11.h>
2023-04-13 00:27:08 +03:00
2023-04-15 21:29:10 +03:00
static void window_process_xcb_event ( UIWindow * window , xcb_generic_event_t * event ) ;
static int window_select_xkb_events_for_device ( UIWindow * window , int32_t device_id ) ;
static int window_keyboard_update_keymap ( UIWindow * window , UIWindowXKBKeyboard * kb ) ;
static int window_keyboard_init ( UIWindow * window , UIWindowXKBKeyboard * kb , uint8_t first_xkb_event , int32_t device_id , struct xkb_context * ctx ) ;
static int window_init_core_xkb_keyboard ( UIWindow * window ) ;
static void window_keyboard_process_event ( UIWindow * window , UIWindowXKBKeyboard * kb , xcb_generic_event_t * gevent ) ;
static void window_keyboard_deinit ( UIWindowXKBKeyboard * kb ) ;
2023-04-13 03:47:38 +03:00
2023-04-13 00:27:08 +03:00
int window_invalidate_node ( UIWindow * window , UINode * node )
{
if ( node - > flags & UI_NODE_COMPONENT ) {
if ( ! node - > parent ) {
return - 1 ;
}
node = node - > parent ;
}
UIRect local_rect = ( UIRect ) {
. x = node - > rect . x + node - > window_rel_x ,
. y = node - > rect . y + node - > window_rel_y ,
. w = node - > rect . w ,
. h = node - > rect . h
} ;
window - > invalid_region = ui_rect_united ( & window - > invalid_region , & local_rect ) ;
return 0 ;
}
void window_free ( UIWindow * window )
{
if ( ! window ) return ;
if ( window - > _cairo_surface ) cairo_surface_destroy ( window - > _cairo_surface ) ;
if ( window - > drw ) cairo_destroy ( window - > drw ) ;
if ( window - > _xcb_connection ) xcb_disconnect ( window - > _xcb_connection ) ;
2023-04-15 21:29:10 +03:00
window_keyboard_deinit ( window - > xkb_core_keyboard ) ;
2023-04-13 00:27:08 +03:00
node_free ( window - > root ) ;
free ( window ) ;
}
UIWindow * window_new ( int width , int height )
{
bool fail = false ;
UIWindow * window = NULL ;
window = calloc ( 1 , sizeof ( UIWindow ) ) ;
if ( ! window ) {
fail = true ;
goto done ;
}
window - > hovered = NULL ;
window - > pressed = NULL ;
window - > root = NULL ;
window - > invalid_region = ( UIRect ) { 0 , 0 , 0 , 0 } ;
window - > _cairo_surface = NULL ;
window - > _xcb_connection = NULL ;
window - > _xcb_window_id = - 1 ;
window - > drw = NULL ;
window - > _xcb_connection = xcb_connect ( NULL , NULL ) ;
if ( xcb_connection_has_error ( window - > _xcb_connection ) ! = 0 ) {
fail = true ;
goto done ;
}
const xcb_setup_t * setup = xcb_get_setup ( window - > _xcb_connection ) ;
xcb_screen_iterator_t iter = xcb_setup_roots_iterator ( setup ) ;
xcb_screen_t * screen = iter . data ;
xcb_visualtype_t * visualtype = NULL ;
for ( xcb_depth_iterator_t iter_depth = xcb_screen_allowed_depths_iterator ( screen ) ; iter_depth . rem ; xcb_depth_next ( & iter_depth ) ) {
for ( xcb_visualtype_iterator_t iter_visualtype = xcb_depth_visuals_iterator ( iter_depth . data ) ; iter_visualtype . rem ; xcb_visualtype_next ( & iter_visualtype ) ) {
if ( iter_visualtype . data - > visual_id = = screen - > root_visual ) {
visualtype = iter_visualtype . data ;
break ;
}
}
if ( visualtype ) break ;
}
if ( ! visualtype ) {
fail = true ;
goto done ;
}
window - > _xcb_window_id = xcb_generate_id ( window - > _xcb_connection ) ;
if ( window - > _xcb_window_id = = ( unsigned ) - 1 ) {
fail = true ;
goto done ;
}
xcb_create_window (
window - > _xcb_connection , // connection
XCB_COPY_FROM_PARENT , // depth
window - > _xcb_window_id , // window id
screen - > root , // parent window
0 , // x
0 , // y
width , // width
height , // height
0 , // border_width
XCB_WINDOW_CLASS_INPUT_OUTPUT , // class
screen - > root_visual , // visual
XCB_CW_EVENT_MASK , // value mask
2023-04-15 21:29:10 +03:00
( uint32_t [ ] ) { XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS } // value list
2023-04-13 00:27:08 +03:00
) ;
xcb_map_window ( window - > _xcb_connection , window - > _xcb_window_id ) ;
if ( xcb_flush ( window - > _xcb_connection ) < = 0 ) {
fail = true ;
goto done ;
}
window - > _cairo_surface = cairo_xcb_surface_create ( window - > _xcb_connection , window - > _xcb_window_id , visualtype , width , height ) ;
if ( cairo_surface_status ( window - > _cairo_surface ) ! = CAIRO_STATUS_SUCCESS ) {
fail = true ;
goto done ;
}
window - > drw = cairo_create ( window - > _cairo_surface ) ;
if ( cairo_status ( window - > drw ) ! = CAIRO_STATUS_SUCCESS ) {
fail = true ;
goto done ;
}
2023-04-15 21:29:10 +03:00
if ( window_init_core_xkb_keyboard ( window ) < 0 ) {
fprintf ( stderr , " err: window_new: failed to initialize keyboard (_window_init_core_xkb_keyboard failed). Keyboard input may not be available. \n " ) ;
}
2023-04-13 00:27:08 +03:00
done :
if ( fail ) {
window_free ( window ) ;
return NULL ;
}
return window ;
}
bool window_flush_invalidated ( UIWindow * window )
{
if ( ui_rect_null ( & window - > invalid_region ) ) {
return false ;
}
2023-04-13 18:34:16 +03:00
begin_clock ( " Flush invalidated widgets " ) ;
2023-04-13 00:27:08 +03:00
cairo_push_group ( window - > drw ) ;
node_repaint ( window - > root , & window - > invalid_region , true , 0 , 0 ) ;
window - > invalid_region = ( UIRect ) { 0 , 0 , 0 , 0 } ;
cairo_pop_group_to_source ( window - > drw ) ;
cairo_paint ( window - > drw ) ;
cairo_surface_flush ( window - > _cairo_surface ) ;
2023-04-13 18:34:16 +03:00
end_clock ( ) ;
2023-04-13 00:27:08 +03:00
return true ;
}
int window_attach_root ( UIWindow * window , UINode * root )
{
if ( ! window | | ! root | | window - > root | | ! window - > drw ) {
return - 1 ;
}
root - > window = window ;
root - > drw = window - > drw ;
root - > width_policy = UI_SIZE_POLICY_STATIC ;
root - > height_policy = UI_SIZE_POLICY_STATIC ;
window - > root = root ;
return 0 ;
}
2023-04-15 21:29:10 +03:00
// Thanks: https://github.com/xkbcommon/libxkbcommon/blob/003fdee1378382d4fef77089f5f4652cd2422c6a/tools/interactive-x11.c#L67
static int window_select_xkb_events_for_device ( UIWindow * window , int32_t device_id )
{
if ( ! window - > _xcb_connection ) {
return - 1 ;
}
enum {
required_events =
( XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
XCB_XKB_EVENT_TYPE_STATE_NOTIFY ) ,
required_nkn_details =
( XCB_XKB_NKN_DETAIL_KEYCODES ) ,
required_map_parts =
( XCB_XKB_MAP_PART_KEY_TYPES |
XCB_XKB_MAP_PART_KEY_SYMS |
XCB_XKB_MAP_PART_MODIFIER_MAP |
XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
XCB_XKB_MAP_PART_KEY_ACTIONS |
XCB_XKB_MAP_PART_VIRTUAL_MODS |
XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP ) ,
required_state_details =
( XCB_XKB_STATE_PART_MODIFIER_BASE |
XCB_XKB_STATE_PART_MODIFIER_LATCH |
XCB_XKB_STATE_PART_MODIFIER_LOCK |
XCB_XKB_STATE_PART_GROUP_BASE |
XCB_XKB_STATE_PART_GROUP_LATCH |
XCB_XKB_STATE_PART_GROUP_LOCK ) ,
} ;
static const xcb_xkb_select_events_details_t details = {
. affectNewKeyboard = required_nkn_details ,
. newKeyboardDetails = required_nkn_details ,
. affectState = required_state_details ,
. stateDetails = required_state_details ,
} ;
xcb_void_cookie_t cookie = xcb_xkb_select_events_aux_checked (
window - > _xcb_connection ,
device_id ,
required_events , /* affectWhich */
0 , /* clear */
0 , /* selectAll */
required_map_parts , /* affectMap */
required_map_parts , /* map */
& details /* details */
) ;
xcb_generic_error_t * error = xcb_request_check ( window - > _xcb_connection , cookie ) ;
if ( error ) {
free ( error ) ;
return - 1 ;
}
return 0 ;
}
static int window_keyboard_update_keymap ( UIWindow * window , UIWindowXKBKeyboard * kb )
{
struct xkb_keymap * keymap = xkb_x11_keymap_new_from_device ( kb - > ctx , window - > _xcb_connection , kb - > device_id , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
if ( ! keymap ) {
return - 1 ;
}
struct xkb_state * state = xkb_x11_state_new_from_device ( keymap , window - > _xcb_connection , kb - > device_id ) ;
if ( ! state ) {
xkb_keymap_unref ( keymap ) ;
return - 1 ;
}
xkb_state_unref ( kb - > state ) ;
xkb_keymap_unref ( kb - > keymap ) ;
kb - > state = state ;
kb - > keymap = keymap ;
return 0 ;
}
static int window_keyboard_init ( UIWindow * window , UIWindowXKBKeyboard * kb , uint8_t first_xkb_event , int32_t device_id , struct xkb_context * ctx )
{
kb - > first_xkb_event = first_xkb_event ;
kb - > device_id = device_id ;
kb - > ctx = ctx ;
kb - > keymap = NULL ;
kb - > state = NULL ;
if ( window_keyboard_update_keymap ( window , kb ) < 0 ) {
return - 1 ;
}
if ( window_select_xkb_events_for_device ( window , device_id ) < 0 ) {
xkb_state_unref ( kb - > state ) ;
xkb_keymap_unref ( kb - > keymap ) ;
return - 1 ;
}
return 0 ;
}
static int window_init_core_xkb_keyboard ( UIWindow * window )
{
uint8_t first_xkb_event ;
int ret = 0 ;
UIWindowXKBKeyboard * kb = NULL ;
struct xkb_context * ctx = NULL ;
if ( ! xkb_x11_setup_xkb_extension (
window - > _xcb_connection ,
XKB_X11_MIN_MAJOR_XKB_VERSION ,
XKB_X11_MIN_MINOR_XKB_VERSION ,
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS ,
NULL , NULL , & first_xkb_event , NULL
) ) {
fprintf ( stderr , " err: _window_init_core_xkb_keyboard: failed to set up xkb extension (xkb_x11_setup_xkb_extension) \n " ) ;
return - 1 ;
}
kb = malloc ( sizeof ( UIWindowXKBKeyboard ) ) ;
if ( ! kb ) {
fprintf ( stderr , " err: _window_init_core_xkb_keyboard: failed to malloc keyboard struct \n " ) ;
ret = - 1 ;
goto defer_error ;
}
ctx = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
if ( ! ctx ) {
fprintf ( stderr , " err: _window_init_core_xkb_keyboard: failed to create xkb context \n " ) ;
ret = - 1 ;
goto defer_error ;
}
int32_t core_kbd_device_id = xkb_x11_get_core_keyboard_device_id ( window - > _xcb_connection ) ;
if ( core_kbd_device_id < 0 ) {
fprintf ( stderr , " err: _window_init_core_xkb_keyboard: failed to find xkb core keyboard device_id \n " ) ;
ret = - 1 ;
goto defer_error ;
}
if ( window_keyboard_init ( window , kb , first_xkb_event , core_kbd_device_id , ctx ) < 0 ) {
fprintf ( stderr , " err: _window_init_core_xkb_keyboard: failed to initialize keyboard (_window_keyboard_init) \n " ) ;
ret = - 1 ;
goto defer_error ;
}
goto success ;
defer_error :
free ( kb ) ;
xkb_context_unref ( ctx ) ;
return ret ;
success :
window - > xkb_core_keyboard = kb ;
return ret ;
}
static void window_keyboard_deinit ( UIWindowXKBKeyboard * kb )
{
if ( ! kb ) {
return ;
}
xkb_state_unref ( kb - > state ) ;
xkb_keymap_unref ( kb - > keymap ) ;
free ( kb ) ;
}
static void window_keyboard_process_event ( UIWindow * window , UIWindowXKBKeyboard * kb , xcb_generic_event_t * gevent )
{
union xkb_event {
struct {
uint8_t response_type ;
uint8_t xkbType ;
uint16_t sequence ;
xcb_timestamp_t time ;
uint8_t deviceID ;
} any ;
xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify ;
xcb_xkb_map_notify_event_t map_notify ;
xcb_xkb_state_notify_event_t state_notify ;
} * event = ( union xkb_event * ) gevent ;
if ( event - > any . deviceID ! = kb - > device_id ) {
return ;
}
switch ( event - > any . xkbType ) {
case XCB_XKB_NEW_KEYBOARD_NOTIFY : {
if ( event - > new_keyboard_notify . changed & & XCB_XKB_NKN_DETAIL_KEYCODES ) {
window_keyboard_update_keymap ( window , kb ) ;
}
break ;
}
case XCB_XKB_MAP_NOTIFY : {
window_keyboard_update_keymap ( window , kb ) ;
break ;
}
case XCB_XKB_STATE_NOTIFY : {
xkb_state_update_mask (
kb - > state ,
event - > state_notify . baseMods ,
event - > state_notify . latchedMods ,
event - > state_notify . lockedMods ,
event - > state_notify . baseGroup ,
event - > state_notify . latchedGroup ,
event - > state_notify . lockedGroup
) ;
break ;
}
}
}
static void window_process_xcb_event ( UIWindow * window , xcb_generic_event_t * event )
2023-04-13 00:27:08 +03:00
{
begin_clock ( " Process XCB event " ) ;
uint8_t event_type = event - > response_type & ~ 0x80 ;
switch ( event_type ) {
case XCB_EXPOSE : {
2023-04-13 04:57:54 +03:00
begin_clock ( " XCB_EXPOSE " ) ;
2023-04-13 04:36:53 +03:00
xcb_expose_event_t * ev = ( xcb_expose_event_t * ) event ;
UIRect region = { ev - > x , ev - > y , ev - > width , ev - > height } ;
window - > invalid_region = ui_rect_united ( & window - > invalid_region , & region ) ;
2023-04-13 00:27:08 +03:00
window_invalidate_node ( window , window - > root ) ;
2023-04-13 04:57:54 +03:00
end_clock ( ) ;
2023-04-13 00:27:08 +03:00
break ;
}
case XCB_CONFIGURE_NOTIFY : {
2023-04-13 04:57:54 +03:00
begin_clock ( " XCB_CONFIGURE_NOTIFY " ) ;
2023-04-13 00:27:08 +03:00
xcb_configure_notify_event_t * ev = ( xcb_configure_notify_event_t * ) event ;
UIRect new_rect = { 0 , 0 , ev - > width , ev - > height } ;
if ( ! ui_rect_equals ( & window - > root - > rect , & new_rect ) ) {
cairo_xcb_surface_set_size ( window - > _cairo_surface , ev - > width , ev - > height ) ;
window - > root - > rect . x = 0 ;
window - > root - > rect . y = 0 ;
window - > root - > rect . w = ev - > width ;
window - > root - > rect . h = ev - > height ;
node_dispatch ( window - > root , UI_EVENT_RELAYOUT , 0 , NULL ) ;
2023-04-13 18:34:16 +03:00
# ifdef _UI_DEBUG
2023-04-13 00:27:08 +03:00
node_dump ( window - > root , 0 ) ;
2023-04-13 18:34:16 +03:00
# endif
2023-04-13 00:27:08 +03:00
}
2023-04-13 04:57:54 +03:00
end_clock ( ) ;
2023-04-13 00:27:08 +03:00
break ;
}
case XCB_MOTION_NOTIFY : {
2023-04-13 04:57:54 +03:00
begin_clock ( " XCB_MOTION_NOTIFY " ) ;
2023-04-13 00:27:08 +03:00
xcb_motion_notify_event_t * ev = ( xcb_motion_notify_event_t * ) event ;
UINode * node = node_by_point ( window - > root , ev - > event_x , ev - > event_y ) ;
if ( node & & node ! = window - > hovered ) {
UINode * previously_hovered = window - > hovered ;
window - > hovered = node ;
if ( previously_hovered ) {
node_dispatch ( previously_hovered , UI_EVENT_UNHOVERED , 0 , NULL ) ;
}
node_dispatch ( node , UI_EVENT_HOVERED , 0 , NULL ) ;
}
2023-04-13 04:57:54 +03:00
end_clock ( ) ;
2023-04-13 00:27:08 +03:00
break ;
}
2023-04-15 21:29:10 +03:00
case XCB_KEY_PRESS : {
begin_clock ( " XCB_KEY_PRESS " ) ;
xcb_key_press_event_t * ev = ( xcb_key_press_event_t * ) event ;
if ( window - > xkb_core_keyboard ) {
UINode * n = window - > pressed ;
while ( n ) {
if ( node_dispatch ( n , UI_EVENT_KEY_DOWN , ev - > detail , window - > xkb_core_keyboard - > state ) > 0 ) {
break ;
}
n = n - > parent ;
}
}
end_clock ( ) ;
break ;
}
2023-04-13 00:27:08 +03:00
case XCB_BUTTON_RELEASE : /* through */
case XCB_BUTTON_PRESS : {
2023-04-13 04:57:54 +03:00
begin_clock ( " XCB_BUTTON_PRESS " ) ;
2023-04-13 00:27:08 +03:00
xcb_button_press_event_t * ev = ( xcb_button_press_event_t * ) event ;
UINode * node = node_by_point ( window - > root , ev - > event_x , ev - > event_y ) ;
if ( node ) {
enum UIEvent ui_event = UI_EVENT_BUTTON_LEFT_UPDATE ;
bool state = event_type = = XCB_BUTTON_PRESS ? true : false ;
double delta = 0.0 ;
switch ( ev - > detail ) {
case XCB_BUTTON_INDEX_3 : {
ui_event = UI_EVENT_BUTTON_RIGHT_UPDATE ;
break ;
}
case XCB_BUTTON_INDEX_4 : {
// scroll up
ui_event = UI_EVENT_SCROLL ;
delta = 20.0 ;
break ;
}
case XCB_BUTTON_INDEX_5 : {
// scroll down
ui_event = UI_EVENT_SCROLL ;
delta = - 20.0 ;
break ;
}
}
UINode * n = node ;
while ( n ) {
if ( node_dispatch ( n , ui_event , state , & delta ) > 0 ) {
break ;
}
n = n - > parent ;
}
if ( ui_event = = UI_EVENT_BUTTON_LEFT_UPDATE ) {
if ( state ) {
UINode * previously_pressed = window - > pressed ;
window - > pressed = node ;
if ( previously_pressed ) {
node_dispatch ( previously_pressed , UI_EVENT_UNPRESSED , 0 , NULL ) ;
}
node_dispatch ( node , UI_EVENT_PRESSED , 0 , NULL ) ;
} else {
UINode * previously_pressed = window - > pressed ;
2023-04-15 21:29:10 +03:00
if ( previously_pressed & & ! ( previously_pressed - > flags & UI_NODE_RETAINS_PRESS ) ) {
window - > pressed = NULL ;
2023-04-13 00:27:08 +03:00
node_dispatch ( previously_pressed , UI_EVENT_UNPRESSED , 0 , NULL ) ;
}
}
}
}
2023-04-13 04:57:54 +03:00
end_clock ( ) ;
2023-04-13 00:27:08 +03:00
break ;
}
default : {
2023-04-15 21:29:10 +03:00
if ( window - > xkb_core_keyboard & & event - > response_type = = window - > xkb_core_keyboard - > first_xkb_event ) {
window_keyboard_process_event ( window , window - > xkb_core_keyboard , event ) ;
}
2023-04-13 00:27:08 +03:00
break ;
}
}
end_clock ( ) ;
}
int window_turn ( UIWindow * window )
{
if ( ! window | | ! window - > root | | ! window - > _xcb_connection | | ! window - > _cairo_surface | | ! window - > drw ) {
return - 1 ;
}
if ( xcb_connection_has_error ( window - > _xcb_connection ) ! = 0 ) {
return - 1 ;
}
if ( cairo_surface_status ( window - > _cairo_surface ) ! = CAIRO_STATUS_SUCCESS | | cairo_status ( window - > drw ) ! = CAIRO_STATUS_SUCCESS ) {
return - 1 ;
}
bool has_looped_once = false ;
struct pollfd fds [ UI_WINDOW_MAX_POLL ] = { 0 } ;
for ( int i = 0 ; i < UI_WINDOW_MAX_POLL ; i + + ) {
fds [ i ] . fd = - 1 ;
fds [ i ] . events = 0 ;
fds [ i ] . revents = 0 ;
}
fds [ 0 ] . fd = xcb_get_file_descriptor ( window - > _xcb_connection ) ;
fds [ 0 ] . events = POLLIN ;
2023-04-13 03:47:38 +03:00
int64_t frame_peak_ms = 0 ;
int64_t frame_peak_last_measurement_ms = 0 ;
2023-04-13 00:27:08 +03:00
for ( ; ; ) {
int64_t poll_timeout ;
/* compute `poll_timeout` based on active timers */
{
2023-04-13 03:47:38 +03:00
int64_t now = time_current_ms ( ) ;
2023-04-13 04:11:15 +03:00
int64_t lowest = INT64_MAX ;
2023-04-13 00:27:08 +03:00
bool has_timer = false ;
for ( int i = 0 ; i < UI_WINDOW_MAX_TIMERS ; i + + ) {
UIWindowTimer * timer = & window - > timers [ i ] ;
if ( timer - > present ) {
has_timer = true ;
int64_t distance = ( timer - > started_at_ms + timer - > duration_ms ) - now ;
if ( distance < lowest ) {
lowest = distance ;
}
}
}
2023-04-13 18:34:16 +03:00
if ( lowest < 0 ) {
lowest = 0 ;
}
2023-04-13 00:27:08 +03:00
poll_timeout = has_timer ? lowest : - 1 ;
if ( ! has_looped_once ) {
// Makes sure that we handle any pending events before entering the loop for the first time
poll_timeout = 0 ;
}
}
{
if ( poll ( fds , UI_WINDOW_MAX_POLL , poll_timeout ) < 0 ) {
fprintf ( stderr , " err: window_turn(): poll() failed \n " ) ;
return - 1 ;
}
}
clear_summary ( ) ;
2023-04-13 04:57:54 +03:00
begin_clock ( " Frame " ) ;
2023-04-13 03:47:38 +03:00
int64_t frame_start_ms = time_current_ms ( ) ;
2023-04-13 00:27:08 +03:00
for ( int i = 0 ; i < UI_WINDOW_MAX_POLL ; i + + ) {
if ( fds [ i ] . revents & POLLNVAL ) {
fprintf ( stderr , " err: window_turn(): poll got POLLNVAL \n " ) ;
return - 1 ;
}
if ( fds [ i ] . revents & POLLERR ) {
fprintf ( stderr , " err: window_turn(): poll got POLLERR \n " ) ;
return - 1 ;
}
if ( fds [ i ] . revents & POLLHUP ) {
fprintf ( stderr , " err: window_turn(): poll got POLLHUP \n " ) ;
return - 1 ;
}
if ( ( i = = 0 & & fds [ i ] . revents & POLLIN ) | | ! has_looped_once ) {
xcb_generic_event_t * xcb_event ;
while ( ( xcb_event = xcb_poll_for_event ( window - > _xcb_connection ) ) ) {
2023-04-15 21:29:10 +03:00
window_process_xcb_event ( window , xcb_event ) ;
2023-04-13 00:27:08 +03:00
free ( xcb_event ) ;
}
}
}
/* process finished timers */
{
for ( int j = 0 ; j < UI_WINDOW_MAX_TIMERS ; j + + ) {
UIWindowTimer * timer = & window - > timers [ j ] ;
if ( timer - > present ) {
2023-04-13 03:47:38 +03:00
int64_t now = time_current_ms ( ) ;
2023-04-13 00:27:08 +03:00
int64_t distance = ( timer - > started_at_ms + timer - > duration_ms ) - now ;
if ( distance < = 0 ) {
2023-04-13 18:34:16 +03:00
int64_t dt = now - timer - > started_at_ms ;
# ifdef _UI_DEBUG
printf ( " timer jitter: %ld \n " , dt - timer - > duration_ms ) ;
# endif
2023-04-13 00:27:08 +03:00
UINode * target = timer - > target ;
timer - > present = false ;
timer - > started_at_ms = 0 ;
timer - > duration_ms = 0 ;
timer - > target = NULL ;
if ( target ) {
node_dispatch ( target , UI_EVENT_TIMER_END , 0 , & dt ) ;
}
}
}
}
}
if ( window_flush_invalidated ( window ) ) {
begin_clock ( " Flush painting results to XCB " ) ;
xcb_flush ( window - > _xcb_connection ) ;
end_clock ( ) ;
}
has_looped_once = true ;
2023-04-13 18:34:16 +03:00
# ifdef _UI_DEBUG
2023-04-13 03:47:38 +03:00
int64_t frame_end_ms = time_current_ms ( ) ;
int64_t frame_delta_ms = frame_end_ms - frame_start_ms ;
2023-04-13 00:27:08 +03:00
if ( frame_delta_ms > frame_peak_ms | | frame_end_ms - frame_peak_last_measurement_ms > = 3000 ) {
frame_peak_last_measurement_ms = frame_end_ms ;
frame_peak_ms = frame_delta_ms ;
printf ( " peak frametime: %ldms \n " , frame_peak_ms ) ;
}
2023-04-13 18:34:16 +03:00
# else
( void ) frame_peak_last_measurement_ms ;
( void ) frame_start_ms ;
( void ) frame_peak_ms ;
# endif
2023-04-13 00:27:08 +03:00
2023-04-13 04:57:54 +03:00
end_clock ( ) ;
dump_summary ( stdout ) ;
2023-04-13 00:27:08 +03:00
}
return 0 ;
}
2023-04-13 03:47:38 +03:00
UIWindowTimer * window_sched_timer ( UIWindow * window , UINode * target , int64_t duration_ms )
2023-04-13 00:27:08 +03:00
{
for ( int i = 0 ; i < UI_WINDOW_MAX_TIMERS ; i + + ) {
if ( ! window - > timers [ i ] . present ) {
UIWindowTimer * timer = & window - > timers [ i ] ;
timer - > present = true ;
timer - > duration_ms = duration_ms ;
timer - > target = target ;
timer - > started_at_ms = time_current_ms ( ) ;
return timer ;
}
}
return NULL ;
}