2016-09-04 20:41:48 +00:00
/*
* Mr . 4 th Dimention - Allen Webster
* ( Mostly by insofaras )
*
* 14.11 .2015
*
2017-01-07 02:59:55 +00:00
* Linux layer for 4 coder
2016-09-04 20:41:48 +00:00
*
*/
// TOP
2017-06-30 21:28:09 +00:00
# define IS_PLAT_LAYER
2017-01-07 02:59:55 +00:00
# include <assert.h>
# include <string.h>
2017-01-23 06:19:43 +00:00
# include "4tech_defines.h"
# include "4coder_API/version.h"
2016-09-04 20:41:48 +00:00
2017-02-18 01:04:41 +00:00
# include "4coder_lib/4coder_utf8.h"
2017-01-23 06:19:43 +00:00
# if defined(FRED_SUPER)
# include "4coder_API / keycodes.h"
# include "4coder_API / style.h"
2016-09-04 20:41:48 +00:00
# define FSTRING_IMPLEMENTATION
# define FSTRING_C
2017-01-23 06:19:43 +00:00
# include "4coder_lib / 4coder_string.h"
# include "4coder_lib / 4coder_mem.h"
2016-09-04 20:41:48 +00:00
2017-01-23 06:19:43 +00:00
# include "4coder_API / types.h"
2016-09-04 20:41:48 +00:00
# include "4ed_os_custom_api.h"
# else
# include "4coder_default_bindings.cpp"
# endif
# include "4ed_math.h"
# include "4ed_system.h"
2017-06-30 19:08:11 +00:00
# include "4ed_log.h"
2016-09-04 20:41:48 +00:00
# include "4ed_rendering.h"
# include "4ed.h"
2017-06-30 22:09:18 +00:00
# include "4ed_file_track.h"
# include "4ed_font_interface_to_os.h"
# include "4ed_system_shared.h"
2017-06-30 21:28:09 +00:00
# include "unix_4ed_functions.cpp"
2016-09-04 20:41:48 +00:00
# include <math.h>
# include <stdlib.h>
2017-03-30 06:40:16 +00:00
# include <time.h>
2016-09-04 20:41:48 +00:00
# include <locale.h>
# include <memory.h>
# include <dlfcn.h>
# include <xmmintrin.h>
# include <pthread.h>
# include <semaphore.h>
# include <signal.h>
# include <ucontext.h>
# include <sys/types.h>
# include <sys/ioctl.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <sys/timerfd.h>
# include <sys/eventfd.h>
# include <sys/epoll.h>
# include <sys/inotify.h>
# include <X11/Xlib.h>
# include <X11/cursorfont.h>
# include <X11/Xatom.h>
# include <X11/extensions/Xfixes.h>
# include <GL/glx.h>
# include <GL/gl.h>
# include <GL/glext.h>
# include <linux/fs.h>
# include <linux/input.h>
//
// Linux macros
//
# define LINUX_MAX_PASTE_CHARS 0x10000L
# define FPS 60L
# define frame_useconds (1000000UL / FPS)
# if FRED_INTERNAL
2017-01-03 20:05:35 +00:00
# define LINUX_FN_DEBUG(fmt, ...) do { \
fprintf ( stderr , " %s: " fmt " \n " , __func__ , # # __VA_ARGS__ ) ; \
} while ( 0 )
2016-09-04 20:41:48 +00:00
# else
2017-01-03 20:05:35 +00:00
# define LINUX_FN_DEBUG(fmt, ...)
2016-09-04 20:41:48 +00:00
# endif
# define InterlockedCompareExchange(dest, ex, comp) __sync_val_compare_and_swap((dest), (comp), (ex))
//
// Linux structs / enums
//
enum {
LINUX_4ED_EVENT_X11 = ( UINT64_C ( 1 ) < < 32 ) ,
LINUX_4ED_EVENT_X11_INTERNAL = ( UINT64_C ( 2 ) < < 32 ) ,
LINUX_4ED_EVENT_STEP = ( UINT64_C ( 3 ) < < 32 ) ,
LINUX_4ED_EVENT_STEP_TIMER = ( UINT64_C ( 4 ) < < 32 ) ,
LINUX_4ED_EVENT_CLI = ( UINT64_C ( 5 ) < < 32 ) ,
} ;
struct Linux_Coroutine {
2017-01-03 20:05:35 +00:00
Coroutine coroutine ;
Linux_Coroutine * next ;
ucontext_t ctx , yield_ctx ;
2016-09-04 20:41:48 +00:00
stack_t stack ;
2017-01-03 20:05:35 +00:00
b32 done ;
2016-09-04 20:41:48 +00:00
} ;
struct Thread_Context {
u32 job_id ;
b32 running ;
b32 cancel ;
Work_Queue * queue ;
u32 id ;
u32 group_id ;
pthread_t handle ;
} ;
struct Thread_Group {
Thread_Context * threads ;
i32 count ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Unbounded_Work_Queue queue ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 cancel_lock0 ;
i32 cancel_cv0 ;
} ;
struct Linux_Vars {
Display * XDisplay ;
Window XWindow ;
Render_Target target ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XIM input_method ;
XIMStyle input_style ;
XIC input_context ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Application_Step_Input input ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
String clipboard_contents ;
String clipboard_outgoing ;
b32 new_clipboard ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom atom_TARGETS ;
Atom atom_CLIPBOARD ;
Atom atom_UTF8_STRING ;
Atom atom__NET_WM_STATE ;
Atom atom__NET_WM_STATE_MAXIMIZED_HORZ ;
Atom atom__NET_WM_STATE_MAXIMIZED_VERT ;
Atom atom__NET_WM_STATE_FULLSCREEN ;
Atom atom__NET_WM_PING ;
Atom atom__NET_WM_WINDOW_TYPE ;
Atom atom__NET_WM_WINDOW_TYPE_NORMAL ;
Atom atom__NET_WM_PID ;
Atom atom_WM_DELETE_WINDOW ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 has_xfixes ;
int xfixes_selection_event ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int epoll ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int step_timer_fd ;
int step_event_fd ;
int x11_fd ;
int inotify_fd ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
u64 last_step ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 keep_running ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Application_Mouse_Cursor cursor ;
b32 hide_cursor ;
Cursor hidden_cursor ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
void * app_code ;
void * custom ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Memory * thread_memory ;
Thread_Group groups [ THREAD_GROUP_COUNT ] ;
Work_Queue queues [ THREAD_GROUP_COUNT ] ;
pthread_mutex_t locks [ LOCK_COUNT ] ;
pthread_cond_t conds [ 8 ] ;
sem_t thread_semaphore ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 dpi_x , dpi_y ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Plat_Settings settings ;
System_Functions system ;
App_Functions app ;
Custom_API custom_api ;
b32 vsync ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Linux_Coroutine coroutine_data [ 18 ] ;
Linux_Coroutine * coroutine_free ;
2017-06-30 19:08:11 +00:00
u32 log_position ;
2016-09-04 20:41:48 +00:00
} ;
//
// Linux globals
//
2017-01-23 06:19:43 +00:00
global Linux_Vars linuxvars ;
global Application_Memory memory_vars ;
2016-09-04 20:41:48 +00:00
//
// Linux forward declarations
//
internal void LinuxScheduleStep ( void ) ;
internal Plat_Handle LinuxSemToHandle ( sem_t * ) ;
internal sem_t * LinuxHandleToSem ( Plat_Handle ) ;
internal Plat_Handle LinuxFDToHandle ( int ) ;
internal int LinuxHandleToFD ( Plat_Handle ) ;
internal void LinuxStringDup ( String * , void * , size_t ) ;
internal void LinuxToggleFullscreen ( Display * , Window ) ;
2016-09-04 21:24:48 +00:00
internal void LinuxFatalErrorMsg ( const char * msg ) ;
2016-09-04 20:41:48 +00:00
internal Sys_Acquire_Lock_Sig ( system_acquire_lock ) ;
internal Sys_Release_Lock_Sig ( system_release_lock ) ;
internal void system_wait_cv ( i32 , i32 ) ;
internal void system_signal_cv ( i32 , i32 ) ;
//
// Time
//
internal
Sys_Now_Time_Sig ( system_now_time ) {
struct timespec spec ;
u64 result ;
clock_gettime ( CLOCK_REALTIME , & spec ) ;
result = ( spec . tv_sec * UINT64_C ( 1000000 ) ) + ( spec . tv_nsec / UINT64_C ( 1000 ) ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
internal
2017-01-23 06:19:43 +00:00
Sys_File_Exists_Sig ( system_file_exists ) {
2016-09-04 20:41:48 +00:00
int result = 0 ;
char buff [ PATH_MAX ] = { } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( len + 1 > PATH_MAX ) {
fputs ( " system_directory_has_file: path too long \n " , stderr ) ;
} else {
memcpy ( buff , filename , len ) ;
buff [ len ] = 0 ;
struct stat st ;
result = stat ( buff , & st ) = = 0 & & S_ISREG ( st . st_mode ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LINUX_FN_DEBUG ( " %s: %d " , buff , result ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
internal
2017-01-23 06:19:43 +00:00
Sys_Directory_CD_Sig ( system_directory_cd ) {
String directory = make_string_cap ( dir , * len , cap ) ;
2016-09-04 20:41:48 +00:00
b32 result = 0 ;
i32 old_size ;
if ( rel_path [ 0 ] ! = 0 ) {
if ( rel_path [ 0 ] = = ' . ' & & rel_path [ 1 ] = = 0 ) {
result = 1 ;
}
else if ( rel_path [ 0 ] = = ' . ' & & rel_path [ 1 ] = = ' . ' & & rel_path [ 2 ] = = 0 ) {
result = remove_last_folder ( & directory ) ;
terminate_with_null ( & directory ) ;
}
else {
if ( directory . size + rel_len + 1 > directory . memory_size ) {
old_size = directory . size ;
append_partial_sc ( & directory , rel_path ) ;
append_s_char ( & directory , ' / ' ) ;
terminate_with_null ( & directory ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct stat st ;
if ( stat ( directory . str , & st ) = = 0 & & S_ISDIR ( st . st_mode ) ) {
result = 1 ;
}
else {
directory . size = old_size ;
}
}
}
}
* len = directory . size ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LINUX_FN_DEBUG ( " %.*s: %d " , directory . size , directory . str , result ) ;
return ( result ) ;
}
internal
2017-01-23 06:19:43 +00:00
Sys_Get_4ed_Path_Sig ( system_get_4ed_path ) {
2016-09-04 20:41:48 +00:00
String str = make_string_cap ( out , 0 , capacity ) ;
return ( system_get_binary_path ( & str ) ) ;
}
internal
2017-01-23 06:19:43 +00:00
Sys_Show_Mouse_Cursor_Sig ( system_show_mouse_cursor ) {
2016-09-04 20:41:48 +00:00
linuxvars . hide_cursor = ! show ;
XDefineCursor ( linuxvars . XDisplay , linuxvars . XWindow , show ? None : linuxvars . hidden_cursor ) ;
}
internal
2017-01-23 06:19:43 +00:00
Sys_Toggle_Fullscreen_Sig ( system_toggle_fullscreen ) {
b32 success = true ;
2016-09-04 20:41:48 +00:00
LinuxToggleFullscreen ( linuxvars . XDisplay , linuxvars . XWindow ) ;
2017-01-23 06:19:43 +00:00
return ( success ) ;
2016-09-04 20:41:48 +00:00
}
internal
2017-01-23 06:19:43 +00:00
Sys_Is_Fullscreen_Sig ( system_is_fullscreen ) {
2017-03-30 06:40:16 +00:00
b32 result = false ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom type , * prop ;
unsigned long nitems , pad ;
int fmt ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
int ret = XGetWindowProperty ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom__NET_WM_STATE , 0 , 32 , False , XA_ATOM , & type , & fmt , & nitems , & pad , ( unsigned char * * ) & prop ) ;
if ( ret = = Success & & prop ) {
result = ( * prop = = linuxvars . atom__NET_WM_STATE_FULLSCREEN ) ;
2016-09-04 20:41:48 +00:00
XFree ( ( unsigned char * ) prop ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return result ;
}
2016-09-10 02:57:45 +00:00
internal
2017-01-23 06:19:43 +00:00
Sys_Send_Exit_Signal_Sig ( system_send_exit_signal ) {
2016-09-10 02:57:45 +00:00
linuxvars . keep_running = 0 ;
}
2016-09-04 20:41:48 +00:00
//
// Clipboard
//
internal
Sys_Post_Clipboard_Sig ( system_post_clipboard ) {
LinuxStringDup ( & linuxvars . clipboard_outgoing , str . str , str . size ) ;
XSetSelectionOwner ( linuxvars . XDisplay , linuxvars . atom_CLIPBOARD , linuxvars . XWindow , CurrentTime ) ;
}
//
// Coroutine
//
internal Linux_Coroutine *
LinuxAllocCoroutine ( ) {
Linux_Coroutine * result = linuxvars . coroutine_free ;
Assert ( result ! = 0 ) ;
2017-01-03 20:05:35 +00:00
if ( getcontext ( & result - > ctx ) = = - 1 ) {
perror ( " getcontext " ) ;
}
2016-09-04 20:41:48 +00:00
result - > ctx . uc_stack = result - > stack ;
linuxvars . coroutine_free = result - > next ;
return ( result ) ;
}
internal void
LinuxFreeCoroutine ( Linux_Coroutine * data ) {
data - > next = linuxvars . coroutine_free ;
linuxvars . coroutine_free = data ;
}
internal void
LinuxCoroutineMain ( void * arg_ ) {
Linux_Coroutine * c = ( Linux_Coroutine * ) arg_ ;
c - > coroutine . func ( & c - > coroutine ) ;
c - > done = 1 ;
LinuxFreeCoroutine ( c ) ;
setcontext ( ( ucontext_t * ) c - > coroutine . yield_handle ) ;
}
internal
Sys_Create_Coroutine_Sig ( system_create_coroutine ) {
Linux_Coroutine * c = LinuxAllocCoroutine ( ) ;
c - > done = 0 ;
2017-01-03 20:05:35 +00:00
makecontext ( & c - > ctx , ( void ( * ) ( ) ) LinuxCoroutineMain , 1 , & c - > coroutine ) ;
2016-09-04 20:41:48 +00:00
* ( ucontext_t * * ) & c - > coroutine . plat_handle = & c - > ctx ;
c - > coroutine . func = func ;
return ( & c - > coroutine ) ;
}
internal
Sys_Launch_Coroutine_Sig ( system_launch_coroutine ) {
Linux_Coroutine * c = ( Linux_Coroutine * ) coroutine ;
ucontext_t * ctx = * ( ucontext * * ) & coroutine - > plat_handle ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
coroutine - > yield_handle = & c - > yield_ctx ;
coroutine - > in = in ;
coroutine - > out = out ;
2017-01-03 20:05:35 +00:00
swapcontext ( & c - > yield_ctx , ctx ) ;
2016-09-04 20:41:48 +00:00
if ( c - > done ) {
LinuxFreeCoroutine ( c ) ;
coroutine = 0 ;
}
return ( coroutine ) ;
}
internal
Sys_Resume_Coroutine_Sig ( system_resume_coroutine ) {
Linux_Coroutine * c = ( Linux_Coroutine * ) coroutine ;
void * fiber ;
Assert ( ! c - > done ) ;
coroutine - > yield_handle = & c - > yield_ctx ;
coroutine - > in = in ;
coroutine - > out = out ;
ucontext * ctx = * ( ucontext * * ) & coroutine - > plat_handle ;
swapcontext ( & c - > yield_ctx , ctx ) ;
if ( c - > done ) {
LinuxFreeCoroutine ( c ) ;
coroutine = 0 ;
}
return ( coroutine ) ;
}
internal
Sys_Yield_Coroutine_Sig ( system_yield_coroutine ) {
2017-01-03 20:05:35 +00:00
swapcontext ( * ( ucontext_t * * ) & coroutine - > plat_handle , ( ucontext * ) coroutine - > yield_handle ) ;
2016-09-04 20:41:48 +00:00
}
//
// CLI
//
internal
Sys_CLI_Call_Sig ( system_cli_call ) {
LINUX_FN_DEBUG ( " %s %s " , path , script_name ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int pipe_fds [ 2 ] ;
if ( pipe ( pipe_fds ) = = - 1 ) {
perror ( " system_cli_call: pipe " ) ;
return 0 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
pid_t child_pid = fork ( ) ;
if ( child_pid = = - 1 ) {
perror ( " system_cli_call: fork " ) ;
return 0 ;
}
enum { PIPE_FD_READ , PIPE_FD_WRITE } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// child
if ( child_pid = = 0 ) {
close ( pipe_fds [ PIPE_FD_READ ] ) ;
dup2 ( pipe_fds [ PIPE_FD_WRITE ] , STDOUT_FILENO ) ;
dup2 ( pipe_fds [ PIPE_FD_WRITE ] , STDERR_FILENO ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( chdir ( path ) = = - 1 ) {
perror ( " system_cli_call: chdir " ) ;
exit ( 1 ) ;
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * argv [ ] = { " sh " , " -c " , script_name , NULL } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( execv ( " /bin/sh " , argv ) = = - 1 ) {
perror ( " system_cli_call: execv " ) ;
}
exit ( 1 ) ;
} else {
close ( pipe_fds [ PIPE_FD_WRITE ] ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
* ( pid_t * ) & cli_out - > proc = child_pid ;
* ( int * ) & cli_out - > out_read = pipe_fds [ PIPE_FD_READ ] ;
* ( int * ) & cli_out - > out_write = pipe_fds [ PIPE_FD_WRITE ] ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct epoll_event e = { } ;
e . events = EPOLLIN | EPOLLET ;
e . data . u64 = LINUX_4ED_EVENT_CLI ;
epoll_ctl ( linuxvars . epoll , EPOLL_CTL_ADD , pipe_fds [ PIPE_FD_READ ] , & e ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return 1 ;
}
internal
Sys_CLI_Begin_Update_Sig ( system_cli_begin_update ) {
// NOTE(inso): I don't think anything needs to be done here.
}
internal
Sys_CLI_Update_Step_Sig ( system_cli_update_step ) {
int pipe_read_fd = * ( int * ) & cli - > out_read ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fd_set fds ;
FD_ZERO ( & fds ) ;
FD_SET ( pipe_read_fd , & fds ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct timeval tv = { } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
size_t space_left = max ;
char * ptr = dest ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
while ( space_left > 0 & & select ( pipe_read_fd + 1 , & fds , NULL , NULL , & tv ) = = 1 ) {
ssize_t num = read ( pipe_read_fd , ptr , space_left ) ;
if ( num = = - 1 ) {
perror ( " system_cli_update_step: read " ) ;
} else if ( num = = 0 ) {
// NOTE(inso): EOF
break ;
} else {
ptr + = num ;
space_left - = num ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
* amount = ( ptr - dest ) ;
return ( ptr - dest ) > 0 ;
}
internal
Sys_CLI_End_Update_Sig ( system_cli_end_update ) {
pid_t pid = * ( pid_t * ) & cli - > proc ;
b32 close_me = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int status ;
if ( pid & & waitpid ( pid , & status , WNOHANG ) > 0 ) {
close_me = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
cli - > exit = WEXITSTATUS ( status ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct epoll_event e = { } ;
epoll_ctl ( linuxvars . epoll , EPOLL_CTL_DEL , * ( int * ) & cli - > out_read , & e ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
close ( * ( int * ) & cli - > out_read ) ;
close ( * ( int * ) & cli - > out_write ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return close_me ;
}
//
// Threads
//
internal
Sys_Acquire_Lock_Sig ( system_acquire_lock ) {
pthread_mutex_lock ( linuxvars . locks + id ) ;
}
internal
Sys_Release_Lock_Sig ( system_release_lock ) {
pthread_mutex_unlock ( linuxvars . locks + id ) ;
}
internal void
system_wait_cv ( i32 lock_id , i32 cv_id ) {
pthread_cond_wait ( linuxvars . conds + cv_id , linuxvars . locks + lock_id ) ;
}
internal void
system_signal_cv ( i32 lock_id , i32 cv_id ) {
pthread_cond_signal ( linuxvars . conds + cv_id ) ;
}
internal void *
JobThreadProc ( void * lpParameter ) {
Thread_Context * thread = ( Thread_Context * ) lpParameter ;
Work_Queue * queue = linuxvars . queues + thread - > group_id ;
Thread_Group * group = linuxvars . groups + thread - > group_id ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 thread_index = thread - > id - 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 cancel_lock = group - > cancel_lock0 + thread_index ;
i32 cancel_cv = group - > cancel_cv0 + thread_index ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Memory * thread_memory = linuxvars . thread_memory + thread_index ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( thread_memory - > size = = 0 ) {
2017-01-23 06:19:43 +00:00
i32 new_size = KB ( 64 ) ;
2017-06-30 19:08:11 +00:00
thread_memory - > data = system_memory_allocate ( new_size ) ;
2016-09-04 20:41:48 +00:00
thread_memory - > size = new_size ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( ; ; ) {
u32 read_index = queue - > read_position ;
u32 write_index = queue - > write_position ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( read_index ! = write_index ) {
// NOTE(allen): Previously I was wrapping by the job wrap then
// wrapping by the queue wrap. That was super stupid what was that?
// Now it just wraps by the queue wrap.
u32 next_read_index = ( read_index + 1 ) % QUEUE_WRAP ;
u32 safe_read_index =
2017-01-03 20:05:35 +00:00
InterlockedCompareExchange ( & queue - > read_position ,
next_read_index , read_index ) ;
2016-09-04 20:41:48 +00:00
if ( safe_read_index = = read_index ) {
Full_Job_Data * full_job = queue - > jobs + safe_read_index ;
// NOTE(allen): This is interlocked so that it plays nice
// with the cancel job routine, which may try to cancel this job
// at the same time that we try to run it
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 safe_running_thread =
2017-01-03 20:05:35 +00:00
InterlockedCompareExchange ( & full_job - > running_thread ,
thread - > id , THREAD_NOT_ASSIGNED ) ;
2016-09-04 20:41:48 +00:00
if ( safe_running_thread = = THREAD_NOT_ASSIGNED ) {
thread - > job_id = full_job - > id ;
thread - > running = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
full_job - > job . callback ( & linuxvars . system ,
thread , thread_memory , full_job - > job . data ) ;
LinuxScheduleStep ( ) ;
//full_job->running_thread = 0;
thread - > running = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( cancel_lock ) ;
if ( thread - > cancel ) {
thread - > cancel = 0 ;
system_signal_cv ( cancel_lock , cancel_cv ) ;
}
system_release_lock ( cancel_lock ) ;
}
}
}
else {
sem_wait ( LinuxHandleToSem ( queue - > semaphore ) ) ;
}
}
}
internal void
initialize_unbounded_queue ( Unbounded_Work_Queue * source_queue ) {
i32 max = 512 ;
2017-03-27 01:52:06 +00:00
source_queue - > jobs = ( Full_Job_Data * ) system_memory_allocate ( max * sizeof ( Full_Job_Data ) ) ;
2016-09-04 20:41:48 +00:00
source_queue - > count = 0 ;
source_queue - > max = max ;
source_queue - > skip = 0 ;
}
inline i32
get_work_queue_available_space ( i32 write , i32 read ) {
// NOTE(allen): The only time that queue->write_position == queue->read_position
// is allowed is when the queue is empty. Thus if
// queue->write_position+1 == queue->read_position the available space is zero.
// So these computations both end up leaving one slot unused. The only way I can
// think to easily eliminate this is to have read and write wrap at twice the size
// of the underlying array but modulo their values into the array then if write
// has caught up with read it still will not be equal... but lots of modulos... ehh.
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 available_space = 0 ;
if ( write > = read ) {
available_space = QUEUE_WRAP - ( write - read ) - 1 ;
}
else {
available_space = ( read - write ) - 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( available_space ) ;
}
# define UNBOUNDED_SKIP_MAX 128
internal void
flush_to_direct_queue ( Unbounded_Work_Queue * source_queue , Work_Queue * queue , i32 thread_count ) {
// NOTE(allen): It is understood that read_position may be changed by other
// threads but it will only make more space in the queue if it is changed.
// Meanwhile write_position should not ever be changed by anything but the
// main thread in this system, so it will not be interlocked.
u32 read_position = queue - > read_position ;
u32 write_position = queue - > write_position ;
u32 available_space = get_work_queue_available_space ( write_position , read_position ) ;
u32 available_jobs = source_queue - > count - source_queue - > skip ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
u32 writable_count = Min ( available_space , available_jobs ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( writable_count > 0 ) {
u32 count1 = writable_count ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( count1 + write_position > QUEUE_WRAP ) {
count1 = QUEUE_WRAP - write_position ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
u32 count2 = writable_count - count1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Full_Job_Data * job_src1 = source_queue - > jobs + source_queue - > skip ;
Full_Job_Data * job_src2 = job_src1 + count1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Full_Job_Data * job_dst1 = queue - > jobs + write_position ;
Full_Job_Data * job_dst2 = queue - > jobs ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Assert ( ( job_src1 - > id % QUEUE_WRAP ) = = write_position ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
memcpy ( job_dst1 , job_src1 , sizeof ( Full_Job_Data ) * count1 ) ;
memcpy ( job_dst2 , job_src2 , sizeof ( Full_Job_Data ) * count2 ) ;
queue - > write_position = ( write_position + writable_count ) % QUEUE_WRAP ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
source_queue - > skip + = writable_count ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( source_queue - > skip = = source_queue - > count ) {
source_queue - > skip = source_queue - > count = 0 ;
}
else if ( source_queue - > skip > UNBOUNDED_SKIP_MAX ) {
u32 left_over = source_queue - > count - source_queue - > skip ;
memmove ( source_queue - > jobs , source_queue - > jobs + source_queue - > skip ,
sizeof ( Full_Job_Data ) * left_over ) ;
source_queue - > count = left_over ;
source_queue - > skip = 0 ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 semaphore_release_count = writable_count ;
if ( semaphore_release_count > thread_count ) {
semaphore_release_count = thread_count ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// NOTE(allen): platform dependent portion...
for ( i32 i = 0 ; i < semaphore_release_count ; + + i ) {
sem_post ( LinuxHandleToSem ( queue - > semaphore ) ) ;
}
}
internal void
flush_thread_group ( i32 group_id ) {
Thread_Group * group = linuxvars . groups + group_id ;
Work_Queue * queue = linuxvars . queues + group_id ;
Unbounded_Work_Queue * source_queue = & group - > queue ;
flush_to_direct_queue ( source_queue , queue , group - > count ) ;
}
// Note(allen): post_job puts the job on the unbounded queue.
// The unbounded queue is entirely managed by the main thread.
// The thread safe queue is bounded in size so the unbounded
// queue is periodically flushed into the direct work queue.
internal
Sys_Post_Job_Sig ( system_post_job ) {
Thread_Group * group = linuxvars . groups + group_id ;
Unbounded_Work_Queue * queue = & group - > queue ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
u32 result = queue - > next_job_id + + ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
while ( queue - > count > = queue - > max ) {
i32 new_max = queue - > max * 2 ;
2017-03-27 01:52:06 +00:00
u32 job_size = sizeof ( Full_Job_Data ) ;
Full_Job_Data * new_jobs = ( Full_Job_Data * ) system_memory_allocate ( new_max * job_size ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
memcpy ( new_jobs , queue - > jobs , queue - > count ) ;
2017-01-03 20:05:35 +00:00
2017-03-27 01:52:06 +00:00
system_memory_free ( queue - > jobs , queue - > max * job_size ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
queue - > jobs = new_jobs ;
queue - > max = new_max ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Full_Job_Data full_job ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
full_job . job = job ;
full_job . running_thread = THREAD_NOT_ASSIGNED ;
full_job . id = result ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
queue - > jobs [ queue - > count + + ] = full_job ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Work_Queue * direct_queue = linuxvars . queues + group_id ;
flush_to_direct_queue ( queue , direct_queue , group - > count ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
internal
Sys_Cancel_Job_Sig ( system_cancel_job ) {
Thread_Group * group = linuxvars . groups + group_id ;
Unbounded_Work_Queue * source_queue = & group - > queue ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 handled_in_unbounded = false ;
if ( source_queue - > skip < source_queue - > count ) {
Full_Job_Data * first_job = source_queue - > jobs + source_queue - > skip ;
if ( first_job - > id < = job_id ) {
u32 index = source_queue - > skip + ( job_id - first_job - > id ) ;
Full_Job_Data * job = source_queue - > jobs + index ;
job - > running_thread = 0 ;
handled_in_unbounded = true ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! handled_in_unbounded ) {
Work_Queue * queue = linuxvars . queues + group_id ;
Full_Job_Data * job = queue - > jobs + ( job_id % QUEUE_WRAP ) ;
Assert ( job - > id = = job_id ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
u32 thread_id =
2017-01-03 20:05:35 +00:00
InterlockedCompareExchange ( & job - > running_thread ,
0 , THREAD_NOT_ASSIGNED ) ;
2016-09-04 20:41:48 +00:00
if ( thread_id ! = THREAD_NOT_ASSIGNED & & thread_id ! = 0 ) {
i32 thread_index = thread_id - 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
i32 cancel_lock = group - > cancel_lock0 + thread_index ;
i32 cancel_cv = group - > cancel_cv0 + thread_index ;
Thread_Context * thread = group - > threads + thread_index ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( cancel_lock ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
thread - > cancel = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_release_lock ( FRAME_LOCK ) ;
do {
system_wait_cv ( cancel_lock , cancel_cv ) ;
} while ( thread - > cancel = = 1 ) ;
system_acquire_lock ( FRAME_LOCK ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_release_lock ( cancel_lock ) ;
}
}
}
internal
Sys_Check_Cancel_Sig ( system_check_cancel ) {
b32 result = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Group * group = linuxvars . groups + thread - > group_id ;
i32 thread_index = thread - > id - 1 ;
i32 cancel_lock = group - > cancel_lock0 + thread_index ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( cancel_lock ) ;
if ( thread - > cancel ) {
result = 1 ;
}
system_release_lock ( cancel_lock ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
internal
Sys_Grow_Thread_Memory_Sig ( system_grow_thread_memory ) {
void * old_data ;
i32 old_size , new_size ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( CANCEL_LOCK0 + memory - > id - 1 ) ;
old_data = memory - > data ;
old_size = memory - > size ;
2017-01-30 16:04:47 +00:00
new_size = l_round_up_i32 ( memory - > size * 2 , KB ( 4 ) ) ;
2017-03-27 01:52:06 +00:00
memory - > data = system_memory_allocate ( new_size ) ;
2016-09-04 20:41:48 +00:00
memory - > size = new_size ;
if ( old_data ) {
memcpy ( memory - > data , old_data , old_size ) ;
2017-03-27 01:52:06 +00:00
system_memory_free ( old_data , old_size ) ;
2016-09-04 20:41:48 +00:00
}
system_release_lock ( CANCEL_LOCK0 + memory - > id - 1 ) ;
}
//
// Debug
//
# if FRED_INTERNAL
# ifdef OLD_JOB_QUEUE
internal
INTERNAL_Sys_Get_Thread_States_Sig ( internal_get_thread_states ) {
Work_Queue * queue = linuxvars . queues + id ;
u32 write = queue - > write_position ;
u32 read = queue - > read_position ;
if ( write < read ) write + = QUEUE_WRAP ;
* pending = ( i32 ) ( write - read ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Group * group = linuxvars . groups + id ;
for ( i32 i = 0 ; i < group - > count ; + + i ) {
running [ i ] = ( group - > threads [ i ] . running ! = 0 ) ;
}
}
# else
internal
INTERNAL_Sys_Get_Thread_States_Sig ( internal_get_thread_states ) {
Thread_Group * group = linuxvars . groups + id ;
Unbounded_Work_Queue * source_queue = & group - > queue ;
Work_Queue * queue = linuxvars . queues + id ;
u32 write = queue - > write_position ;
u32 read = queue - > read_position ;
if ( write < read ) write + = QUEUE_WRAP ;
* pending = ( i32 ) ( write - read ) + source_queue - > count - source_queue - > skip ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( i32 i = 0 ; i < group - > count ; + + i ) {
running [ i ] = ( group - > threads [ i ] . running ! = 0 ) ;
}
}
# endif
# endif
//
// Linux rendering/font system functions
//
2017-03-27 01:52:06 +00:00
# include "4ed_font_data.h"
2017-01-14 03:01:35 +00:00
# include "4ed_system_shared.cpp"
2016-09-04 20:41:48 +00:00
//
// End of system funcs
//
//
// Linux init functions
//
internal b32
LinuxLoadAppCode ( String * base_dir ) {
b32 result = 0 ;
App_Get_Functions * get_funcs = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! sysshared_to_binary_path ( base_dir , " 4ed_app.so " ) ) {
return 0 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . app_code = dlopen ( base_dir - > str , RTLD_LAZY ) ;
if ( linuxvars . app_code ) {
get_funcs = ( App_Get_Functions * )
dlsym ( linuxvars . app_code , " app_get_functions " ) ;
} else {
fprintf ( stderr , " dlopen failed: %s \n " , dlerror ( ) ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( get_funcs ) {
result = 1 ;
linuxvars . app = get_funcs ( ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
internal void
LinuxLoadSystemCode ( ) {
// files
linuxvars . system . set_file_list = system_set_file_list ;
linuxvars . system . get_canonical = system_get_canonical ;
linuxvars . system . add_listener = system_add_listener ;
linuxvars . system . remove_listener = system_remove_listener ;
linuxvars . system . get_file_change = system_get_file_change ;
linuxvars . system . load_handle = system_load_handle ;
linuxvars . system . load_size = system_load_size ;
linuxvars . system . load_file = system_load_file ;
linuxvars . system . load_close = system_load_close ;
linuxvars . system . save_file = system_save_file ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// time
linuxvars . system . now_time = system_now_time ;
2017-01-03 20:05:35 +00:00
2017-01-23 06:19:43 +00:00
// custom.h
2016-09-04 20:41:48 +00:00
linuxvars . system . memory_allocate = system_memory_allocate ;
linuxvars . system . memory_set_protection = system_memory_set_protection ;
linuxvars . system . memory_free = system_memory_free ;
linuxvars . system . file_exists = system_file_exists ;
linuxvars . system . directory_cd = system_directory_cd ;
linuxvars . system . get_4ed_path = system_get_4ed_path ;
linuxvars . system . show_mouse_cursor = system_show_mouse_cursor ;
linuxvars . system . toggle_fullscreen = system_toggle_fullscreen ;
linuxvars . system . is_fullscreen = system_is_fullscreen ;
2016-09-10 02:57:45 +00:00
linuxvars . system . send_exit_signal = system_send_exit_signal ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// clipboard
linuxvars . system . post_clipboard = system_post_clipboard ;
// coroutine
linuxvars . system . create_coroutine = system_create_coroutine ;
linuxvars . system . launch_coroutine = system_launch_coroutine ;
linuxvars . system . resume_coroutine = system_resume_coroutine ;
linuxvars . system . yield_coroutine = system_yield_coroutine ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// cli
linuxvars . system . cli_call = system_cli_call ;
linuxvars . system . cli_begin_update = system_cli_begin_update ;
linuxvars . system . cli_update_step = system_cli_update_step ;
linuxvars . system . cli_end_update = system_cli_end_update ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// threads
linuxvars . system . post_job = system_post_job ;
linuxvars . system . cancel_job = system_cancel_job ;
linuxvars . system . check_cancel = system_check_cancel ;
linuxvars . system . grow_thread_memory = system_grow_thread_memory ;
linuxvars . system . acquire_lock = system_acquire_lock ;
linuxvars . system . release_lock = system_release_lock ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// debug
# if FRED_INTERNAL
linuxvars . system . internal_get_thread_states = internal_get_thread_states ;
# endif
}
internal void
LinuxLoadRenderCode ( ) {
linuxvars . target . push_clip = draw_push_clip ;
linuxvars . target . pop_clip = draw_pop_clip ;
linuxvars . target . push_piece = draw_push_piece ;
}
//
// Renderer
//
internal void
LinuxRedrawTarget ( ) {
2017-03-19 18:25:12 +00:00
launch_rendering ( & linuxvars . system , & linuxvars . target ) ;
2016-09-04 20:41:48 +00:00
//glFlush();
glXSwapBuffers ( linuxvars . XDisplay , linuxvars . XWindow ) ;
}
internal void
LinuxResizeTarget ( i32 width , i32 height ) {
if ( width > 0 & & height > 0 ) {
glViewport ( 0 , 0 , width , height ) ;
glMatrixMode ( GL_PROJECTION ) ;
glLoadIdentity ( ) ;
glOrtho ( 0 , width , height , 0 , - 1 , 1 ) ;
glScissor ( 0 , 0 , width , height ) ;
linuxvars . target . width = width ;
linuxvars . target . height = height ;
}
}
//
// OpenGL init
//
// NOTE(allen): Thanks to Casey for providing the linux OpenGL launcher.
static bool ctxErrorOccurred = false ;
internal int
ctxErrorHandler ( Display * dpy , XErrorEvent * ev )
{
ctxErrorOccurred = true ;
return 0 ;
}
# if FRED_INTERNAL
static void LinuxGLDebugCallback (
2017-01-03 20:05:35 +00:00
GLenum source ,
GLenum type ,
GLuint id ,
GLenum severity ,
GLsizei length ,
const GLchar * message ,
const void * userParam
2016-09-04 20:41:48 +00:00
) {
fprintf ( stderr , " GL DEBUG: %s \n " , message ) ;
}
# endif
internal GLXContext
InitializeOpenGLContext ( Display * XDisplay , Window XWindow , GLXFBConfig & bestFbc , b32 & IsLegacy )
{
IsLegacy = false ;
typedef GLXContext ( * glXCreateContextAttribsARBProc ) ( Display * , GLXFBConfig , GLXContext , Bool , const int * ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
typedef PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXTProc ;
typedef PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESAProc ;
typedef PFNGLXGETSWAPINTERVALMESAPROC glXGetSwapIntervalMESAProc ;
typedef PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGIProc ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
const char * glxExts = glXQueryExtensionsString ( XDisplay , DefaultScreen ( XDisplay ) ) ;
2017-01-03 20:05:35 +00:00
# define GLXLOAD(x) x ## Proc x = (x ## Proc) glXGetProcAddressARB( (const GLubyte*) #x);
2016-09-04 20:41:48 +00:00
GLXLOAD ( glXCreateContextAttribsARB ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
GLXContext ctx = 0 ;
ctxErrorOccurred = false ;
int ( * oldHandler ) ( Display * , XErrorEvent * ) = XSetErrorHandler ( & ctxErrorHandler ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! glXCreateContextAttribsARB )
{
fprintf ( stderr , " glXCreateContextAttribsARB() not found, using old-style GLX context \n " ) ;
ctx = glXCreateNewContext ( XDisplay , bestFbc , GLX_RGBA_TYPE , 0 , True ) ;
}
else
{
int context_attribs [ ] =
{
GLX_CONTEXT_MAJOR_VERSION_ARB , 4 ,
GLX_CONTEXT_MINOR_VERSION_ARB , 3 ,
GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB ,
# if FRED_INTERNAL
GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB ,
# endif
None
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " Creating GL 4.3 context... \n " ) ;
ctx = glXCreateContextAttribsARB ( XDisplay , bestFbc , 0 , True , context_attribs ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSync ( XDisplay , False ) ;
if ( ! ctxErrorOccurred & & ctx )
{
fprintf ( stderr , " Created GL 4.3 context. \n " ) ;
}
else
{
ctxErrorOccurred = false ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
context_attribs [ 1 ] = 3 ;
context_attribs [ 3 ] = 2 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " GL 4.3 unavailable, creating GL 3.2 context... \n " ) ;
ctx = glXCreateContextAttribsARB ( XDisplay , bestFbc , 0 , True , context_attribs ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSync ( XDisplay , False ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! ctxErrorOccurred & & ctx )
{
fprintf ( stderr , " Created GL 3.2 context. \n " ) ;
}
else
{
context_attribs [ 1 ] = 1 ;
context_attribs [ 3 ] = 2 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
ctxErrorOccurred = false ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " Failed to create GL 3.2 context, using old-style GLX context \n " ) ;
ctx = glXCreateContextAttribsARB ( XDisplay , bestFbc , 0 , True , context_attribs ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
IsLegacy = true ;
}
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSync ( XDisplay , False ) ;
XSetErrorHandler ( oldHandler ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ctxErrorOccurred | | ! ctx )
{
fprintf ( stderr , " Failed to create an OpenGL context \n " ) ;
exit ( 1 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 Direct ;
if ( ! glXIsDirect ( XDisplay , ctx ) )
{
fprintf ( stderr , " Indirect GLX rendering context obtained \n " ) ;
Direct = 0 ;
}
else
{
fprintf ( stderr , " Direct GLX rendering context obtained \n " ) ;
Direct = 1 ;
}
fprintf ( stderr , " Making context current \n " ) ;
glXMakeCurrent ( XDisplay , XWindow , ctx ) ;
char * Vendor = ( char * ) glGetString ( GL_VENDOR ) ;
char * Renderer = ( char * ) glGetString ( GL_RENDERER ) ;
char * Version = ( char * ) glGetString ( GL_VERSION ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//TODO(inso): glGetStringi is required in core profile if the GL version is >= 3.0
char * Extensions = ( char * ) glGetString ( GL_EXTENSIONS ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " GL_VENDOR: %s \n " , Vendor ) ;
fprintf ( stderr , " GL_RENDERER: %s \n " , Renderer ) ;
fprintf ( stderr , " GL_VERSION: %s \n " , Version ) ;
// fprintf(stderr, "GL_EXTENSIONS: %s\n", Extensions);
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): enable vsync if available. this should probably be optional
if ( Direct & & strstr ( glxExts , " GLX_EXT_swap_control " ) ) {
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
GLXLOAD ( glXSwapIntervalEXT ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( glXSwapIntervalEXT ) {
glXSwapIntervalEXT ( XDisplay , XWindow , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
unsigned int swap_val = 0 ;
glXQueryDrawable ( XDisplay , XWindow , GLX_SWAP_INTERVAL_EXT , & swap_val ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . vsync = swap_val = = 1 ;
fprintf ( stderr , " VSync enabled? %s. \n " , linuxvars . vsync ? " Yes " : " No " ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
} else if ( Direct & & strstr ( glxExts , " GLX_MESA_swap_control " ) ) {
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
GLXLOAD ( glXSwapIntervalMESA ) ;
GLXLOAD ( glXGetSwapIntervalMESA ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( glXSwapIntervalMESA ) {
glXSwapIntervalMESA ( 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( glXGetSwapIntervalMESA ) {
linuxvars . vsync = glXGetSwapIntervalMESA ( ) ;
fprintf ( stderr , " VSync enabled? %s (MESA) \n " , linuxvars . vsync ? " Yes " : " No " ) ;
} else {
// NOTE(inso): assume it worked?
linuxvars . vsync = 1 ;
fputs ( " VSync enabled? possibly (MESA) \n " , stderr ) ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
} else if ( Direct & & strstr ( glxExts , " GLX_SGI_swap_control " ) ) {
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
GLXLOAD ( glXSwapIntervalSGI ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( glXSwapIntervalSGI ) {
glXSwapIntervalSGI ( 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): The SGI one doesn't seem to have a way to confirm we got it...
linuxvars . vsync = 1 ;
fputs ( " VSync enabled? hopefully (SGI) \n " , stderr ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
} else {
fputs ( " VSync enabled? nope, no suitable extension \n " , stderr ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# if FRED_INTERNAL
typedef PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackProc ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
GLXLOAD ( glDebugMessageCallback ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( glDebugMessageCallback ) {
fputs ( " Enabling GL Debug Callback \n " , stderr ) ;
glDebugMessageCallback ( & LinuxGLDebugCallback , 0 ) ;
glEnable ( GL_DEBUG_OUTPUT ) ;
}
# endif
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
glEnable ( GL_TEXTURE_2D ) ;
glEnable ( GL_SCISSOR_TEST ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# undef GLXLOAD
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( ctx ) ;
}
internal b32
GLXCanUseFBConfig ( Display * XDisplay )
{
b32 Result = false ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int GLXMajor , GLXMinor ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * XVendor = ServerVendor ( XDisplay ) ;
fprintf ( stderr , " XWindows vendor: %s \n " , XVendor ) ;
if ( glXQueryVersion ( XDisplay , & GLXMajor , & GLXMinor ) )
{
fprintf ( stderr , " GLX version %d.%d \n " , GLXMajor , GLXMinor ) ;
if ( ( ( GLXMajor = = 1 ) & & ( GLXMinor > = 3 ) ) | | ( GLXMajor > 1 ) )
{
Result = true ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( Result ) ;
}
typedef struct glx_config_result {
b32 Found ;
GLXFBConfig BestConfig ;
XVisualInfo BestInfo ;
} glx_config_result ;
internal glx_config_result
ChooseGLXConfig ( Display * XDisplay , int XScreenIndex )
{
glx_config_result Result = { 0 } ;
int DesiredAttributes [ ] =
2017-01-03 20:05:35 +00:00
{
GLX_X_RENDERABLE , True ,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT ,
GLX_RENDER_TYPE , GLX_RGBA_BIT ,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR ,
GLX_RED_SIZE , 8 ,
GLX_GREEN_SIZE , 8 ,
GLX_BLUE_SIZE , 8 ,
GLX_ALPHA_SIZE , 8 ,
GLX_DEPTH_SIZE , 24 ,
GLX_STENCIL_SIZE , 8 ,
GLX_DOUBLEBUFFER , True ,
//GLX_SAMPLE_BUFFERS , 1,
//GLX_SAMPLES , 4,
None
} ;
int ConfigCount = 0 ;
GLXFBConfig * Configs = glXChooseFBConfig ( XDisplay ,
XScreenIndex ,
DesiredAttributes ,
& ConfigCount ) ;
if ( Configs & & ConfigCount > 0 )
{
2016-09-04 20:41:48 +00:00
XVisualInfo * VI = glXGetVisualFromFBConfig ( XDisplay , Configs [ 0 ] ) ;
if ( VI )
{
Result . Found = true ;
Result . BestConfig = Configs [ 0 ] ;
2017-01-03 20:05:35 +00:00
Result . BestInfo = * VI ;
2016-09-04 20:41:48 +00:00
int id = 0 ;
glXGetFBConfigAttrib ( XDisplay , Result . BestConfig , GLX_FBCONFIG_ID , & id ) ;
fprintf ( stderr , " Using FBConfig: %d (0x%x) \n " , id , id ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFree ( VI ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFree ( Configs ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( Result ) ;
}
//
// X11 input / events init
//
struct Init_Input_Result {
XIM input_method ;
XIMStyle best_style ;
XIC xic ;
} ;
2017-03-30 06:40:16 +00:00
static Init_Input_Result null_init_input_result = { 0 } ;
2016-09-04 20:41:48 +00:00
internal Init_Input_Result
2017-03-30 06:40:16 +00:00
LinuxInputInit ( Display * dpy , Window XWindow ) {
2016-09-04 20:41:48 +00:00
Init_Input_Result result = { } ;
XIMStyles * styles = 0 ;
XIMStyle style ;
unsigned long xim_event_mask = 0 ;
2017-01-03 20:05:35 +00:00
2017-05-29 17:00:18 +00:00
setlocale ( LC_ALL , " " ) ;
XSetLocaleModifiers ( " " ) ;
2017-05-29 15:51:26 +00:00
b32 locale_supported = XSupportsLocale ( ) ;
fprintf ( stderr , " Supported locale?: %s. \n " , locale_supported ? " Yes " : " No " ) ;
if ( ! locale_supported ) {
2017-05-29 17:00:18 +00:00
fprintf ( stderr , " Reverting to 'C' ... " ) ;
setlocale ( LC_ALL , " C " ) ;
2017-05-29 15:51:26 +00:00
locale_supported = XSupportsLocale ( ) ;
2017-05-29 17:00:18 +00:00
fprintf ( stderr , " C is supported? %s. \n " , locale_supported ? " Yes " : " No " ) ;
2017-05-29 15:51:26 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
result . input_method = XOpenIM ( dpy , 0 , 0 , 0 ) ;
if ( ! result . input_method ) {
// NOTE(inso): Try falling back to the internal XIM implementation that
// should in theory always exist.
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSetLocaleModifiers ( " @im=none " ) ;
result . input_method = XOpenIM ( dpy , 0 , 0 , 0 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( result . input_method ) {
if ( ! XGetIMValues ( result . input_method , XNQueryInputStyle , & styles , NULL ) & & styles ) {
2017-03-30 06:40:16 +00:00
for ( i32 i = 0 ; i < styles - > count_styles ; + + i ) {
2016-09-04 20:41:48 +00:00
style = styles - > supported_styles [ i ] ;
if ( style = = ( XIMPreeditNothing | XIMStatusNothing ) ) {
result . best_style = style ;
break ;
}
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( result . best_style ) {
XFree ( styles ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
result . xic = XCreateIC ( result . input_method , XNInputStyle , result . best_style , XNClientWindow , XWindow , XNFocusWindow , XWindow , NULL ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( XGetICValues ( result . xic , XNFilterEvents , & xim_event_mask , NULL ) ) {
xim_event_mask = 0 ;
}
}
else {
2017-03-30 06:40:16 +00:00
result = null_init_input_result ;
2016-09-04 20:41:48 +00:00
fputs ( " Could not get minimum required input style. \n " , stderr ) ;
2017-05-29 15:51:26 +00:00
exit ( 1 ) ;
2016-09-04 20:41:48 +00:00
}
}
else {
2017-03-30 06:40:16 +00:00
result = null_init_input_result ;
fprintf ( stderr , " Could not open X Input Method. \n " ) ;
2017-05-29 15:51:26 +00:00
exit ( 1 ) ;
2017-03-30 06:40:16 +00:00
}
u32 flags = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask | MappingNotify | ExposureMask | VisibilityChangeMask | xim_event_mask ;
XSelectInput ( linuxvars . XDisplay , linuxvars . XWindow , flags ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return ( result ) ;
}
//
// Keyboard handling funcs
//
2017-02-12 06:01:01 +00:00
global Key_Code keycode_lookup_table [ 255 ] ;
2016-09-04 20:41:48 +00:00
internal void
LinuxKeycodeInit ( Display * dpy ) {
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// NOTE(inso): This looks a bit dumb, but it's the best way I can think of to do it, since:
// KeySyms are the type representing "virtual" keys, like XK_BackSpace, but they are 32-bit ints.
// KeyCodes are guaranteed to fit in 1 byte (and therefore the keycode_lookup_table) but
// have dynamic numbers assigned by the XServer.
// There is XKeysymToKeycode, but it only returns 1 KeyCode for a KeySym. I have my capslock
// rebound to esc, so there are two KeyCodes for the XK_Escape KeyCode but XKeysymToKeycode only
// gets one of them, hence the need for this crazy lookup which works correctly with rebound keys.
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
memset ( keycode_lookup_table , 0 , sizeof ( keycode_lookup_table ) ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct SymMapping {
KeySym sym ;
2017-02-12 06:59:51 +00:00
u16 code ;
2016-09-04 20:41:48 +00:00
} sym_table [ ] = {
{ XK_BackSpace , key_back } ,
{ XK_Delete , key_del } ,
{ XK_Up , key_up } ,
{ XK_Down , key_down } ,
{ XK_Left , key_left } ,
{ XK_Right , key_right } ,
{ XK_Insert , key_insert } ,
{ XK_Home , key_home } ,
{ XK_End , key_end } ,
{ XK_Page_Up , key_page_up } ,
{ XK_Page_Down , key_page_down } ,
{ XK_Escape , key_esc } ,
{ XK_F1 , key_f1 } ,
{ XK_F2 , key_f2 } ,
{ XK_F3 , key_f3 } ,
{ XK_F4 , key_f4 } ,
{ XK_F5 , key_f5 } ,
{ XK_F6 , key_f6 } ,
{ XK_F7 , key_f7 } ,
{ XK_F8 , key_f8 } ,
{ XK_F9 , key_f9 } ,
{ XK_F10 , key_f10 } ,
{ XK_F11 , key_f11 } ,
{ XK_F12 , key_f12 } ,
{ XK_F13 , key_f13 } ,
{ XK_F14 , key_f14 } ,
{ XK_F15 , key_f15 } ,
{ XK_F16 , key_f16 } ,
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
const int table_size = sizeof ( sym_table ) / sizeof ( struct SymMapping ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int key_min , key_max , syms_per_code ;
XDisplayKeycodes ( dpy , & key_min , & key_max ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int key_count = ( key_max - key_min ) + 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
KeySym * syms = XGetKeyboardMapping (
dpy ,
key_min ,
key_count ,
& syms_per_code
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
if ( ! syms ) return ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int key = key_min ;
for ( int i = 0 ; i < key_count * syms_per_code ; + + i ) {
for ( int j = 0 ; j < table_size ; + + j ) {
if ( sym_table [ j ] . sym = = syms [ i ] ) {
keycode_lookup_table [ key + ( i / syms_per_code ) ] = sym_table [ j ] . code ;
break ;
}
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFree ( syms ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
}
internal void
2017-02-12 06:59:51 +00:00
LinuxPushKey ( Key_Code code , Key_Code chr , Key_Code chr_nocaps , b8 ( * mods ) [ MDFR_INDEX_COUNT ] )
2016-09-04 20:41:48 +00:00
{
2017-01-30 16:04:47 +00:00
i32 * count = & linuxvars . input . keys . count ;
Key_Event_Data * data = linuxvars . input . keys . keys ;
2017-01-03 20:05:35 +00:00
if ( * count < KEY_INPUT_BUFFER_SIZE ) {
data [ * count ] . keycode = code ;
data [ * count ] . character = chr ;
data [ * count ] . character_no_caps_lock = chr_nocaps ;
memcpy ( data [ * count ] . modifiers , * mods , sizeof ( * mods ) ) ;
+ + ( * count ) ;
}
2016-09-04 20:41:48 +00:00
}
//
// Misc utility funcs
//
internal Plat_Handle
LinuxSemToHandle ( sem_t * sem ) {
return * ( Plat_Handle * ) & sem ;
}
internal sem_t *
LinuxHandleToSem ( Plat_Handle h ) {
return * ( sem_t * * ) & h ;
}
internal Plat_Handle
LinuxFDToHandle ( int fd ) {
return * ( Plat_Handle * ) & fd ;
}
internal int
LinuxHandleToFD ( Plat_Handle h ) {
return * ( int * ) & h ;
}
internal void
LinuxStringDup ( String * str , void * data , size_t size ) {
2017-06-30 19:08:11 +00:00
if ( str - > memory_size < size ) {
if ( str - > str ) {
system_memory_free ( str - > str , str - > memory_size ) ;
2016-09-04 20:41:48 +00:00
}
str - > memory_size = size ;
2017-06-30 19:08:11 +00:00
str - > str = ( char * ) system_memory_allocate ( size ) ;
2016-09-04 20:41:48 +00:00
//TODO(inso): handle alloc failure case
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
str - > size = size ;
memcpy ( str - > str , data , size ) ;
}
internal void
2017-03-30 06:40:16 +00:00
LinuxScheduleStep ( void ) {
2016-09-04 20:41:48 +00:00
u64 now = system_now_time ( ) ;
u64 diff = ( now - linuxvars . last_step ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
if ( diff > ( u64 ) frame_useconds ) {
2016-09-04 20:41:48 +00:00
u64 ev = 1 ;
2016-09-04 23:58:49 +00:00
ssize_t size = write ( linuxvars . step_event_fd , & ev , sizeof ( ev ) ) ;
( void ) size ;
2017-03-30 06:40:16 +00:00
}
else {
2016-09-04 20:41:48 +00:00
struct itimerspec its = { } ;
timerfd_gettime ( linuxvars . step_timer_fd , & its ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
if ( its . it_value . tv_sec = = 0 & & its . it_value . tv_nsec = = 0 ) {
2016-09-04 20:41:48 +00:00
its . it_value . tv_nsec = ( frame_useconds - diff ) * 1000UL ;
timerfd_settime ( linuxvars . step_timer_fd , 0 , & its , NULL ) ;
}
}
}
//
// X11 utility funcs
//
internal void
2017-03-29 22:35:13 +00:00
LinuxSetWMState ( Display * d , Window w , Atom one , Atom two , int mode ) {
2016-09-04 20:41:48 +00:00
//NOTE(inso): this will only work after it is mapped
enum { STATE_REMOVE , STATE_ADD , STATE_TOGGLE } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XEvent e = { } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
e . xany . type = ClientMessage ;
e . xclient . message_type = linuxvars . atom__NET_WM_STATE ;
e . xclient . format = 32 ;
e . xclient . window = w ;
e . xclient . data . l [ 0 ] = mode ;
e . xclient . data . l [ 1 ] = one ;
e . xclient . data . l [ 2 ] = two ;
e . xclient . data . l [ 3 ] = 1L ;
2017-01-03 20:05:35 +00:00
2017-03-29 22:35:13 +00:00
XSendEvent ( d , RootWindow ( d , 0 ) , 0 , SubstructureNotifyMask | SubstructureRedirectMask , & e ) ;
2016-09-04 20:41:48 +00:00
}
internal void
LinuxMaximizeWindow ( Display * d , Window w , b32 maximize )
{
2017-03-29 22:35:13 +00:00
LinuxSetWMState ( d , w , linuxvars . atom__NET_WM_STATE_MAXIMIZED_HORZ , linuxvars . atom__NET_WM_STATE_MAXIMIZED_VERT , maximize ! = 0 ) ;
2016-09-04 20:41:48 +00:00
}
internal void
LinuxToggleFullscreen ( Display * d , Window w )
{
LinuxSetWMState ( d , w , linuxvars . atom__NET_WM_STATE_FULLSCREEN , 0 , 2 ) ;
}
# include "linux_icon.h"
internal void
LinuxSetIcon ( Display * d , Window w )
{
Atom WM_ICON = XInternAtom ( d , " _NET_WM_ICON " , False ) ;
2017-03-29 22:35:13 +00:00
XChangeProperty ( d , w , WM_ICON , XA_CARDINAL , 32 , PropModeReplace , ( unsigned char * ) linux_icon , sizeof ( linux_icon ) / sizeof ( long ) ) ;
2016-09-04 20:41:48 +00:00
}
internal void
LinuxX11ConnectionWatch ( Display * dpy , XPointer cdata , int fd , Bool opening , XPointer * wdata )
{
struct epoll_event e = { } ;
e . events = EPOLLIN | EPOLLET ;
e . data . u64 = LINUX_4ED_EVENT_X11_INTERNAL | fd ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int op = opening ? EPOLL_CTL_ADD : EPOLL_CTL_DEL ;
epoll_ctl ( linuxvars . epoll , op , fd , & e ) ;
}
// NOTE(inso): this was a quick hack, might need some cleanup.
internal void
LinuxFatalErrorMsg ( const char * msg )
{
fprintf ( stderr , " Fatal Error: %s \n " , msg ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Display * dpy = XOpenDisplay ( 0 ) ;
if ( ! dpy ) {
exit ( 1 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
const int num_cols = 50 ;
int win_w = ( num_cols + 10 ) * 9 ;
int win_h = 140 ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
{
const char * start_p = msg , * space_p = NULL ;
for ( const char * p = msg ; * p ; + + p ) {
if ( * p = = ' ' ) space_p = p ;
if ( * p = = ' \n ' | | p - start_p > num_cols ) {
win_h + = 18 ;
start_p = space_p ? space_p + 1 : p ;
space_p = NULL ;
}
}
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
Window w = XCreateSimpleWindow ( dpy , DefaultRootWindow ( dpy ) , 0 , 0 , win_w , win_h , 0 , 0 , 0x227A3B ) ;
2016-09-04 20:41:48 +00:00
XStoreName ( dpy , w , " 4coder Error " ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSizeHints * sh = XAllocSizeHints ( ) ;
sh - > flags = PMinSize ;
sh - > min_width = win_w ;
sh - > min_height = win_h ;
XSetWMNormalHints ( dpy , w , sh ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom type = XInternAtom ( dpy , " _NET_WM_WINDOW_TYPE_DIALOG " , False ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XChangeProperty ( dpy , w ,
XInternAtom ( dpy , " _NET_WM_WINDOW_TYPE " , False ) ,
XA_ATOM ,
32 ,
PropModeReplace ,
( unsigned char * ) & type ,
1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom WM_DELETE_WINDOW = XInternAtom ( dpy , " WM_DELETE_WINDOW " , False ) ;
XSetWMProtocols ( dpy , w , & WM_DELETE_WINDOW , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
LinuxSetIcon ( dpy , w ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XMapRaised ( dpy , w ) ;
XSync ( dpy , False ) ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
XSelectInput ( dpy , w , ExposureMask | StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask ) ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
XFontStruct * font = XLoadQueryFont ( dpy , " -*-fixed-bold-*-*-*-*-140-*-*-*-*-iso8859-1 " ) ;
2016-09-04 20:41:48 +00:00
if ( ! font ) {
exit ( 1 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XGCValues gcv ;
gcv . foreground = WhitePixel ( dpy , 0 ) ;
gcv . line_width = 2 ;
gcv . font = font - > fid ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
GC gc1 = XCreateGC ( dpy , w , GCForeground | GCFont | GCLineWidth , & gcv ) ;
2016-09-04 20:41:48 +00:00
gcv . foreground = BlackPixel ( dpy , 0 ) ;
2016-09-10 07:27:21 +00:00
GC gc2 = XCreateGC ( dpy , w , GCForeground | GCFont | GCLineWidth , & gcv ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int button_trigger = 0 ;
int button_hi = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XEvent ev ;
2017-03-30 06:40:16 +00:00
while ( 1 ) {
2016-09-04 20:41:48 +00:00
XNextEvent ( dpy , & ev ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int redraw = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = Expose ) redraw = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = ConfigureNotify ) {
redraw = 1 ;
win_w = ev . xconfigure . width ;
win_h = ev . xconfigure . height ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XRectangle button_rect = { win_w / 2 - 40 , win_h * 0.8f , 80 , 20 } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = MotionNotify ) {
int new_hi = ( ev . xmotion . x > button_rect . x & &
ev . xmotion . y > button_rect . y & &
ev . xmotion . x < button_rect . x + button_rect . width & &
ev . xmotion . y < button_rect . y + button_rect . height ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( new_hi ! = button_hi ) {
button_hi = new_hi ;
redraw = 1 ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
if ( ev . type = = KeyPress ) {
KeySym sym = XLookupKeysym ( & ev . xkey , 0 ) ;
if ( sym = = XK_Escape | | sym = = XK_Return ) {
exit ( 1 ) ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = ButtonPress & & ev . xbutton . button = = Button1 ) {
if ( button_hi ) button_trigger = 1 ;
redraw = 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = ButtonRelease & & ev . xbutton . button = = Button1 ) {
if ( button_trigger ) {
if ( button_hi ) {
exit ( 1 ) ;
} else {
button_trigger = 0 ;
}
}
redraw = 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ev . type = = ClientMessage & & ev . xclient . window = = w & & ( Atom ) ev . xclient . data . l [ 0 ] = = WM_DELETE_WINDOW ) {
exit ( 1 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
# define DRAW_STR(x, y, str, len) \
XDrawString ( dpy , w , gc2 , ( x ) + 1 , ( y ) + 1 , ( str ) , ( len ) ) ; \
XDrawString ( dpy , w , gc1 , ( x ) , ( y ) , ( str ) , ( len ) )
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( redraw ) {
XClearWindow ( dpy , w ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
const char * line_start = msg ;
const char * last_space = NULL ;
int y = 30 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
{
const char title [ ] = " 4coder - Fatal Error " ;
int width = XTextWidth ( font , title , sizeof ( title ) - 1 ) ;
int x = ( win_w / 2 ) - ( width / 2 ) ;
2016-09-10 07:27:21 +00:00
DRAW_STR ( x , y , title , sizeof ( title ) - 1 ) ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
y + = 36 ;
2016-09-10 07:27:21 +00:00
int width = XTextWidth ( font , " x " , 1 ) * num_cols ;
2016-09-04 20:41:48 +00:00
int x = ( win_w / 2 ) - ( width / 2 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( const char * p = line_start ; * p ; + + p ) {
if ( * p = = ' ' ) last_space = p ;
2016-09-10 07:27:21 +00:00
if ( p - line_start > num_cols | | * p = = ' \n ' | | ! p [ 1 ] ) {
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
const char * new_line_start = last_space + 1 ;
if ( ! last_space | | * p = = ' \n ' | | ! p [ 1 ] ) {
new_line_start = last_space = ( p + ! p [ 1 ] ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
DRAW_STR ( x , y , line_start , last_space - line_start ) ;
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
line_start = new_line_start ;
2016-09-04 20:41:48 +00:00
last_space = NULL ;
y + = 18 ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
XDrawRectangles ( dpy , w , gc1 , & button_rect , 1 ) ;
2016-09-04 20:41:48 +00:00
if ( button_hi | | button_trigger ) {
XDrawRectangle ( dpy , w , gc2 , button_rect . x + 1 , button_rect . y + 1 , button_rect . width - 2 , button_rect . height - 2 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-10 07:27:21 +00:00
DRAW_STR ( button_rect . x + 20 , button_rect . y + 15 , " Drat! " , 5 ) ;
2016-09-04 20:41:48 +00:00
}
}
2016-09-10 07:27:21 +00:00
# undef DRAW_STR
2016-09-04 20:41:48 +00:00
}
internal int
LinuxGetXSettingsDPI ( Display * dpy , int screen )
{
struct XSettingHeader {
u8 type ;
u8 pad0 ;
u16 name_len ;
char name [ 0 ] ;
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct XSettings {
u8 byte_order ;
u8 pad [ 3 ] ;
u32 serial ;
u32 num_settings ;
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
enum { XSettingsTypeInt , XSettingsTypeString , XSettingsTypeColor } ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int dpi = - 1 ;
unsigned char * prop = NULL ;
char sel_buffer [ 64 ] ;
struct XSettings * xs ;
const char * p ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
snprintf ( sel_buffer , sizeof ( sel_buffer ) , " _XSETTINGS_S%d " , screen ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom XSET_SEL = XInternAtom ( dpy , sel_buffer , True ) ;
Atom XSET_SET = XInternAtom ( dpy , " _XSETTINGS_SETTINGS " , True ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( XSET_SEL = = None | | XSET_SET = = None ) {
fputs ( " XSETTINGS unavailable. \n " , stderr ) ;
return dpi ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Window xset_win = XGetSelectionOwner ( dpy , XSET_SEL ) ;
if ( xset_win = = None ) {
// TODO(inso): listen for the ClientMessage about it becoming available?
// there's not much point atm if DPI scaling is only done at startup
goto out ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
{
Atom type ;
int fmt ;
unsigned long pad , num ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( XGetWindowProperty ( dpy , xset_win , XSET_SET , 0 , 1024 , False , XSET_SET , & type , & fmt , & num , & pad , & prop ) ! = Success ) {
fputs ( " XSETTINGS: GetWindowProperty failed. \n " , stderr ) ;
goto out ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( fmt ! = 8 ) {
fputs ( " XSETTINGS: Wrong format. \n " , stderr ) ;
goto out ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
xs = ( struct XSettings * ) prop ;
p = ( char * ) ( xs + 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( xs - > byte_order ! = 0 ) {
fputs ( " FIXME: XSETTINGS not host byte order? \n " , stderr ) ;
goto out ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( int i = 0 ; i < xs - > num_settings ; + + i ) {
struct XSettingHeader * h = ( struct XSettingHeader * ) p ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// const char* strs[] = { "Int", "String", "Color" };
// printf("%s:\t\"%.*s\"\n", strs[h->type], h->name_len, h->name);
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
p + = sizeof ( struct XSettingHeader ) ;
p + = h - > name_len ;
p + = ( ( 4 - ( h - > name_len & 3 ) ) & 3 ) ;
p + = 4 ; // serial
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
switch ( h - > type ) {
case XSettingsTypeInt : {
if ( strncmp ( h - > name , " Xft/DPI " , h - > name_len ) = = 0 ) {
dpi = * ( i32 * ) p ;
if ( dpi ! = - 1 ) dpi / = 1024 ;
}
p + = 4 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case XSettingsTypeString : {
u32 len = * ( u32 * ) p ;
p + = 4 ;
p + = len ;
p + = ( ( 4 - ( len & 3 ) ) & 3 ) ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case XSettingsTypeColor : {
p + = 8 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
default : {
fputs ( " XSETTINGS: Got invalid type... \n " , stderr ) ;
goto out ;
} break ;
}
}
2017-01-03 20:05:35 +00:00
out :
2016-09-04 20:41:48 +00:00
if ( prop ) {
XFree ( prop ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return dpi ;
}
//
// X11 window init
//
2017-05-23 15:37:58 +00:00
internal f32
2017-03-19 18:25:12 +00:00
size_change ( i32 x , i32 y ) {
f32 xs = x / 96.f ;
f32 ys = y / 96.f ;
f32 s = Min ( xs , ys ) ;
2017-05-23 15:37:58 +00:00
return ( s ) ;
2017-03-19 18:25:12 +00:00
}
2016-09-04 20:41:48 +00:00
# define BASE_W 800
# define BASE_H 600
2017-05-18 21:28:33 +00:00
internal b32
LinuxX11WindowInit ( int argc , char * * argv , int * window_width , int * window_height ) {
2016-09-04 20:41:48 +00:00
if ( linuxvars . settings . set_window_size ) {
2017-05-18 21:28:33 +00:00
* window_width = linuxvars . settings . window_w ;
* window_height = linuxvars . settings . window_h ;
2016-09-04 20:41:48 +00:00
} else {
2017-05-23 15:37:58 +00:00
f32 schange = size_change ( linuxvars . dpi_x , linuxvars . dpi_y ) ;
* window_width = ceil32 ( BASE_W * schange ) ;
* window_height = ceil32 ( BASE_H * schange ) ;
2016-09-04 20:41:48 +00:00
}
2017-05-23 15:37:58 +00:00
* window_width = Max ( * window_width , 1 ) ;
* window_height = Max ( * window_height , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! GLXCanUseFBConfig ( linuxvars . XDisplay ) ) {
LinuxFatalErrorMsg ( " Your XServer's GLX version is too old. GLX 1.3+ is required. " ) ;
return false ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
glx_config_result Config = ChooseGLXConfig ( linuxvars . XDisplay , DefaultScreen ( linuxvars . XDisplay ) ) ;
if ( ! Config . Found ) {
LinuxFatalErrorMsg ( " Could not get a matching GLX FBConfig. Check your OpenGL drivers are installed correctly. " ) ;
return false ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSetWindowAttributes swa = { } ;
swa . backing_store = WhenMapped ;
swa . event_mask = StructureNotifyMask ;
swa . bit_gravity = NorthWestGravity ;
2017-03-13 23:48:11 +00:00
swa . colormap = XCreateColormap ( linuxvars . XDisplay , RootWindow ( linuxvars . XDisplay , Config . BestInfo . screen ) , Config . BestInfo . visual , AllocNone ) ;
2017-05-18 21:28:33 +00:00
u32 CWflags = CWBackingStore | CWBitGravity | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask ;
linuxvars . XWindow = XCreateWindow ( linuxvars . XDisplay , RootWindow ( linuxvars . XDisplay , Config . BestInfo . screen ) , 0 , 0 , * window_width , * window_height , 0 , Config . BestInfo . depth , InputOutput , Config . BestInfo . visual , CWflags , & swa ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! linuxvars . XWindow ) {
LinuxFatalErrorMsg ( " XCreateWindow failed. Make sure your display is set up correctly. " ) ;
return false ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): Set the window's type to normal
2017-03-29 22:35:13 +00:00
XChangeProperty ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom__NET_WM_WINDOW_TYPE , XA_ATOM , 32 , PropModeReplace , ( unsigned char * ) & linuxvars . atom__NET_WM_WINDOW_TYPE_NORMAL , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): window managers want the PID as a window property for some reason.
pid_t pid = getpid ( ) ;
2017-03-29 22:35:13 +00:00
XChangeProperty ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom__NET_WM_PID , XA_CARDINAL , 32 , PropModeReplace , ( unsigned char * ) & pid , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# define WINDOW_NAME "4coder 4linux: " VERSION
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): set wm properties
XStoreName ( linuxvars . XDisplay , linuxvars . XWindow , WINDOW_NAME ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSizeHints * sz_hints = XAllocSizeHints ( ) ;
XWMHints * wm_hints = XAllocWMHints ( ) ;
XClassHint * cl_hints = XAllocClassHint ( ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
sz_hints - > flags = PMinSize | PMaxSize | PWinGravity ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
sz_hints - > min_width = 50 ;
sz_hints - > min_height = 50 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
sz_hints - > max_width = sz_hints - > max_height = ( 1UL < < 16UL ) ;
2017-01-03 20:05:35 +00:00
/* NOTE(inso): fluxbox thinks this is minimum, so don't set it
sz_hints - > base_width = BASE_W ;
sz_hints - > base_height = BASE_H ;
*/
2016-09-04 20:41:48 +00:00
sz_hints - > win_gravity = NorthWestGravity ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . settings . set_window_pos ) {
sz_hints - > flags | = USPosition ;
sz_hints - > x = linuxvars . settings . window_x ;
sz_hints - > y = linuxvars . settings . window_y ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
wm_hints - > flags | = InputHint | StateHint ;
wm_hints - > input = True ;
wm_hints - > initial_state = NormalState ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
cl_hints - > res_name = " 4coder " ;
cl_hints - > res_class = " 4coder " ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * win_name_list [ ] = { WINDOW_NAME } ;
XTextProperty win_name ;
XStringListToTextProperty ( win_name_list , 1 , & win_name ) ;
2017-01-03 20:05:35 +00:00
2017-03-29 22:35:13 +00:00
XSetWMProperties ( linuxvars . XDisplay , linuxvars . XWindow , & win_name , NULL , argv , argc , sz_hints , wm_hints , cl_hints ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFree ( win_name . value ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFree ( sz_hints ) ;
XFree ( wm_hints ) ;
XFree ( cl_hints ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LinuxSetIcon ( linuxvars . XDisplay , linuxvars . XWindow ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): make the window visible
XMapWindow ( linuxvars . XDisplay , linuxvars . XWindow ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 IsLegacy = false ;
GLXContext GLContext =
InitializeOpenGLContext ( linuxvars . XDisplay , linuxvars . XWindow , Config . BestConfig , IsLegacy ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XRaiseWindow ( linuxvars . XDisplay , linuxvars . XWindow ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . settings . set_window_pos ) {
2017-03-29 22:35:13 +00:00
XMoveWindow ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . settings . window_x , linuxvars . settings . window_y ) ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . settings . maximize_window ) {
LinuxMaximizeWindow ( linuxvars . XDisplay , linuxvars . XWindow , 1 ) ;
}
else if ( linuxvars . settings . fullscreen_window ) {
LinuxToggleFullscreen ( linuxvars . XDisplay , linuxvars . XWindow ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSync ( linuxvars . XDisplay , False ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XWindowAttributes WinAttribs ;
if ( XGetWindowAttributes ( linuxvars . XDisplay , linuxvars . XWindow , & WinAttribs ) )
{
2017-05-18 21:28:33 +00:00
* window_width = WinAttribs . width ;
* window_height = WinAttribs . height ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Atom wm_protos [ ] = {
linuxvars . atom_WM_DELETE_WINDOW ,
linuxvars . atom__NET_WM_PING
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSetWMProtocols ( linuxvars . XDisplay , linuxvars . XWindow , wm_protos , 2 ) ;
2017-01-03 20:05:35 +00:00
2016-09-05 02:44:50 +00:00
return true ;
2016-09-04 20:41:48 +00:00
}
internal void
LinuxHandleX11Events ( void )
{
static XEvent PrevEvent = { } ;
b32 should_step = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
while ( XPending ( linuxvars . XDisplay ) )
{
XEvent Event ;
XNextEvent ( linuxvars . XDisplay , & Event ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( XFilterEvent ( & Event , None ) = = True ) {
continue ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
switch ( Event . type ) {
case KeyPress : {
should_step = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 is_hold = ( PrevEvent . type = = KeyRelease & &
PrevEvent . xkey . time = = Event . xkey . time & &
PrevEvent . xkey . keycode = = Event . xkey . keycode ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b8 mods [ MDFR_INDEX_COUNT ] = { } ;
mods [ MDFR_HOLD_INDEX ] = is_hold ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( Event . xkey . state & ShiftMask ) mods [ MDFR_SHIFT_INDEX ] = 1 ;
if ( Event . xkey . state & ControlMask ) mods [ MDFR_CONTROL_INDEX ] = 1 ;
if ( Event . xkey . state & LockMask ) mods [ MDFR_CAPS_INDEX ] = 1 ;
if ( Event . xkey . state & Mod1Mask ) mods [ MDFR_ALT_INDEX ] = 1 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Event . xkey . state & = ~ ( ControlMask ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Status status ;
KeySym keysym = NoSymbol ;
2017-02-13 03:08:57 +00:00
u8 buff [ 32 ] = { } ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
Xutf8LookupString ( linuxvars . input_context , & Event . xkey , ( char * ) buff , sizeof ( buff ) - 1 , & keysym , & status ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( status = = XBufferOverflow ) {
//TODO(inso): handle properly
Xutf8ResetIC ( linuxvars . input_context ) ;
XSetICFocus ( linuxvars . input_context ) ;
fputs ( " FIXME: XBufferOverflow from LookupString. \n " , stderr ) ;
}
2017-01-03 20:05:35 +00:00
2017-02-20 21:05:42 +00:00
u32 key = utf8_to_u32_unchecked ( buff ) ;
u32 key_no_caps = key ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( mods [ MDFR_CAPS_INDEX ] & & status = = XLookupBoth & & Event . xkey . keycode ) {
2017-02-20 21:05:42 +00:00
u8 buff_no_caps [ 32 ] = { 0 } ;
2016-09-04 20:41:48 +00:00
Event . xkey . state & = ~ ( LockMask ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
XLookupString ( & Event . xkey , ( char * ) buff_no_caps , sizeof ( buff_no_caps ) - 1 , NULL , NULL ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( * buff_no_caps ) {
2017-02-20 21:05:42 +00:00
key_no_caps = utf8_to_u32_unchecked ( buff_no_caps ) ;
2016-09-04 20:41:48 +00:00
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( key = = ' \r ' ) key = ' \n ' ;
if ( key_no_caps = = ' \r ' ) key_no_caps = ' \n ' ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// don't push modifiers
if ( keysym > = XK_Shift_L & & keysym < = XK_Hyper_R ) {
break ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( keysym = = XK_ISO_Left_Tab ) {
key = key_no_caps = ' \t ' ;
mods [ MDFR_SHIFT_INDEX ] = 1 ;
}
2017-01-03 20:05:35 +00:00
2017-02-12 06:59:51 +00:00
Key_Code special_key = keycode_lookup_table [ ( u8 ) Event . xkey . keycode ] ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( special_key ) {
2017-02-12 06:59:51 +00:00
LinuxPushKey ( special_key , 0 , 0 , & mods ) ;
2017-02-13 03:08:57 +00:00
} else if ( key < 256 ) {
2017-02-12 06:59:51 +00:00
LinuxPushKey ( key , key , key_no_caps , & mods ) ;
2016-09-04 20:41:48 +00:00
} else {
2017-02-12 06:59:51 +00:00
LinuxPushKey ( 0 , 0 , 0 , & mods ) ;
2016-09-04 20:41:48 +00:00
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case KeyRelease : {
should_step = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case MotionNotify : {
should_step = 1 ;
linuxvars . input . mouse . x = Event . xmotion . x ;
linuxvars . input . mouse . y = Event . xmotion . y ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case ButtonPress : {
should_step = 1 ;
switch ( Event . xbutton . button ) {
case Button1 : {
linuxvars . input . mouse . press_l = 1 ;
linuxvars . input . mouse . l = 1 ;
} break ;
case Button3 : {
linuxvars . input . mouse . press_r = 1 ;
linuxvars . input . mouse . r = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): scroll up
case Button4 : {
linuxvars . input . mouse . wheel = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//NOTE(inso): scroll down
case Button5 : {
linuxvars . input . mouse . wheel = - 1 ;
} break ;
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case ButtonRelease : {
should_step = 1 ;
switch ( Event . xbutton . button ) {
case Button1 : {
linuxvars . input . mouse . release_l = 1 ;
linuxvars . input . mouse . l = 0 ;
} break ;
case Button3 : {
linuxvars . input . mouse . release_r = 1 ;
linuxvars . input . mouse . r = 0 ;
} break ;
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case EnterNotify : {
should_step = 1 ;
linuxvars . input . mouse . out_of_window = 0 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case LeaveNotify : {
should_step = 1 ;
linuxvars . input . mouse . out_of_window = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case FocusIn :
case FocusOut : {
should_step = 1 ;
linuxvars . input . mouse . l = 0 ;
linuxvars . input . mouse . r = 0 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case ConfigureNotify : {
should_step = 1 ;
2017-03-30 06:40:16 +00:00
i32 w = Event . xconfigure . width ;
i32 h = Event . xconfigure . height ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( w ! = linuxvars . target . width | | h ! = linuxvars . target . height ) {
2017-03-30 06:40:16 +00:00
LinuxResizeTarget ( w , h ) ;
2016-09-04 20:41:48 +00:00
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case MappingNotify : {
if ( Event . xmapping . request = = MappingModifier | | Event . xmapping . request = = MappingKeyboard ) {
XRefreshKeyboardMapping ( & Event . xmapping ) ;
LinuxKeycodeInit ( linuxvars . XDisplay ) ;
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case ClientMessage : {
if ( ( Atom ) Event . xclient . data . l [ 0 ] = = linuxvars . atom_WM_DELETE_WINDOW ) {
should_step = 1 ;
linuxvars . keep_running = 0 ;
}
else if ( ( Atom ) Event . xclient . data . l [ 0 ] = = linuxvars . atom__NET_WM_PING ) {
Event . xclient . window = DefaultRootWindow ( linuxvars . XDisplay ) ;
XSendEvent (
linuxvars . XDisplay ,
Event . xclient . window ,
False ,
SubstructureRedirectMask | SubstructureNotifyMask ,
& Event
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
}
} break ;
2017-01-03 20:05:35 +00:00
// NOTE(inso): Someone wants us to give them the clipboard data.
2016-09-04 20:41:48 +00:00
case SelectionRequest : {
XSelectionRequestEvent request = Event . xselectionrequest ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSelectionEvent response = { } ;
response . type = SelectionNotify ;
response . requestor = request . requestor ;
response . selection = request . selection ;
response . target = request . target ;
response . time = request . time ;
response . property = None ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if (
linuxvars . clipboard_outgoing . size & &
request . selection = = linuxvars . atom_CLIPBOARD & &
request . property ! = None & &
request . display & &
request . requestor
2017-01-03 20:05:35 +00:00
) {
2016-09-04 20:41:48 +00:00
Atom atoms [ ] = {
XA_STRING ,
linuxvars . atom_UTF8_STRING
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( request . target = = linuxvars . atom_TARGETS ) {
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XChangeProperty (
request . display ,
request . requestor ,
request . property ,
XA_ATOM ,
32 ,
PropModeReplace ,
( u8 * ) atoms ,
ArrayCount ( atoms )
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
response . property = request . property ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
} else {
b32 found = false ;
for ( int i = 0 ; i < ArrayCount ( atoms ) ; + + i ) {
if ( request . target = = atoms [ i ] ) {
found = true ;
break ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( found ) {
XChangeProperty (
request . display ,
request . requestor ,
request . property ,
request . target ,
8 ,
PropModeReplace ,
( u8 * ) linuxvars . clipboard_outgoing . str ,
linuxvars . clipboard_outgoing . size
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
response . property = request . property ;
}
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XSendEvent ( request . display , request . requestor , True , 0 , ( XEvent * ) & response ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
} break ;
2017-01-03 20:05:35 +00:00
// NOTE(inso): Another program is now the clipboard owner.
2016-09-04 20:41:48 +00:00
case SelectionClear : {
if ( Event . xselectionclear . selection = = linuxvars . atom_CLIPBOARD ) {
linuxvars . clipboard_outgoing . size = 0 ;
}
} break ;
2017-01-03 20:05:35 +00:00
// NOTE(inso): A program is giving us the clipboard data we asked for.
2016-09-04 20:41:48 +00:00
case SelectionNotify : {
XSelectionEvent * e = ( XSelectionEvent * ) & Event ;
2017-03-30 06:40:16 +00:00
if ( e - > selection = = linuxvars . atom_CLIPBOARD & & e - > target = = linuxvars . atom_UTF8_STRING & & e - > property ! = None ) {
2016-09-04 20:41:48 +00:00
Atom type ;
int fmt ;
unsigned long nitems , bytes_left ;
u8 * data ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
int result = XGetWindowProperty ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom_CLIPBOARD , 0L , LINUX_MAX_PASTE_CHARS / 4L , False , linuxvars . atom_UTF8_STRING , & type , & fmt , & nitems , & bytes_left , & data ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( result = = Success & & fmt = = 8 ) {
LinuxStringDup ( & linuxvars . clipboard_contents , data , nitems ) ;
should_step = 1 ;
linuxvars . new_clipboard = 1 ;
XFree ( data ) ;
XDeleteProperty ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom_CLIPBOARD ) ;
}
}
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case Expose :
case VisibilityNotify : {
should_step = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
default : {
if ( Event . type = = linuxvars . xfixes_selection_event ) {
XFixesSelectionNotifyEvent * sne = ( XFixesSelectionNotifyEvent * ) & Event ;
if ( sne - > subtype = = XFixesSelectionNotify & & sne - > owner ! = linuxvars . XWindow ) {
2017-03-30 06:40:16 +00:00
XConvertSelection ( linuxvars . XDisplay , linuxvars . atom_CLIPBOARD , linuxvars . atom_UTF8_STRING , linuxvars . atom_CLIPBOARD , linuxvars . XWindow , CurrentTime ) ;
2016-09-04 20:41:48 +00:00
}
}
} break ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
PrevEvent = Event ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( should_step ) {
LinuxScheduleStep ( ) ;
}
}
//
// Entry point
//
int
2017-03-30 06:40:16 +00:00
main ( int argc , char * * argv ) {
2016-09-04 20:41:48 +00:00
//
// System & Memory init
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char base_dir_mem [ PATH_MAX ] ;
String base_dir = make_fixed_width_string ( base_dir_mem ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( ! LinuxLoadAppCode ( & base_dir ) ) {
LinuxFatalErrorMsg ( " Could not load '4ed_app.so'. This file should be in the same directory as the main '4ed' executable. " ) ;
return 99 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LinuxLoadSystemCode ( ) ;
LinuxLoadRenderCode ( ) ;
2017-01-03 20:05:35 +00:00
2017-01-23 06:19:43 +00:00
memory_vars . vars_memory_size = MB ( 2 ) ;
2017-03-27 01:52:06 +00:00
memory_vars . vars_memory = system_memory_allocate ( memory_vars . vars_memory_size ) ;
2017-01-23 06:19:43 +00:00
memory_vars . target_memory_size = MB ( 512 ) ;
2017-03-27 01:52:06 +00:00
memory_vars . target_memory = system_memory_allocate ( memory_vars . target_memory_size ) ;
2017-01-23 06:19:43 +00:00
memory_vars . user_memory_size = MB ( 2 ) ;
2017-03-27 01:52:06 +00:00
memory_vars . user_memory = system_memory_allocate ( memory_vars . user_memory_size ) ;
2017-01-03 20:05:35 +00:00
2017-01-23 06:19:43 +00:00
linuxvars . target . max = MB ( 1 ) ;
2017-03-27 01:52:06 +00:00
linuxvars . target . push_buffer = ( char * ) system_memory_allocate ( linuxvars . target . max ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( memory_vars . vars_memory = = NULL | | memory_vars . target_memory = = NULL | | memory_vars . user_memory = = NULL | | linuxvars . target . push_buffer = = NULL ) {
LinuxFatalErrorMsg ( " Could not allocate sufficient memory. Please make sure you have atleast 512Mb of RAM free. (This requirement will be relaxed in the future). " ) ;
exit ( 1 ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
init_shared_vars ( ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// Read command line
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * cwd = get_current_dir_name ( ) ;
if ( ! cwd ) {
char buf [ 1024 ] ;
snprintf ( buf , sizeof ( buf ) , " Call to get_current_dir_name failed: %s " , strerror ( errno ) ) ;
LinuxFatalErrorMsg ( buf ) ;
return 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
String current_directory = make_string_slowly ( cwd ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Command_Line_Parameters clparams ;
clparams . argv = argv ;
clparams . argc = argc ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * * files ;
i32 * file_count ;
i32 output_size ;
2017-01-03 20:05:35 +00:00
2017-03-29 22:35:13 +00:00
output_size = linuxvars . app . read_command_line ( & linuxvars . system , & memory_vars , current_directory , & linuxvars . settings , & files , & file_count , clparams ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( output_size > 0 ) {
// TODO(allen): crt free version
fprintf ( stdout , " %.*s " , output_size , ( char * ) memory_vars . target_memory ) ;
}
if ( output_size ! = 0 ) {
LinuxFatalErrorMsg ( " Error reading command-line arguments. " ) ;
return 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
sysshared_filter_real_files ( files , file_count ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// Custom layer linkage
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# ifdef FRED_SUPER
2017-01-03 20:05:35 +00:00
2017-01-23 06:19:43 +00:00
char * custom_file_default = " custom_4coder.so " ;
2016-09-04 20:41:48 +00:00
sysshared_to_binary_path ( & base_dir , custom_file_default ) ;
custom_file_default = base_dir . str ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
char * custom_file ;
if ( linuxvars . settings . custom_dll ) {
custom_file = linuxvars . settings . custom_dll ;
} else {
custom_file = custom_file_default ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . custom = dlopen ( custom_file , RTLD_LAZY ) ;
if ( ! linuxvars . custom & & custom_file ! = custom_file_default ) {
if ( ! linuxvars . settings . custom_dll_is_strict ) {
linuxvars . custom = dlopen ( custom_file_default , RTLD_LAZY ) ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . custom ) {
linuxvars . custom_api . get_alpha_4coder_version = ( _Get_Version_Function * )
dlsym ( linuxvars . custom , " get_alpha_4coder_version " ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . custom_api . get_alpha_4coder_version = = 0 | |
linuxvars . custom_api . get_alpha_4coder_version ( MAJOR , MINOR , PATCH ) = = 0 ) {
2017-01-23 06:19:43 +00:00
LinuxFatalErrorMsg ( " Failed to load 'custom_4coder.so': Version mismatch. Try rebuilding it with 'buildsuper.sh'. " ) ;
2016-09-04 20:41:48 +00:00
exit ( 1 ) ;
}
else {
linuxvars . custom_api . get_bindings = ( Get_Binding_Data_Function * )
dlsym ( linuxvars . custom , " get_bindings " ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . custom_api . get_bindings = = 0 ) {
2017-01-23 06:19:43 +00:00
LinuxFatalErrorMsg ( " Failed to load 'custom_4coder.so': "
2016-09-04 20:41:48 +00:00
" It does not export the required 'get_bindings' function. "
" Try rebuilding it with 'buildsuper.sh'. " ) ;
exit ( 1 ) ;
}
else {
2017-01-23 06:19:43 +00:00
fprintf ( stderr , " Successfully loaded custom_4coder.so \n " ) ;
2016-09-04 20:41:48 +00:00
}
}
} else {
char buf [ 4096 ] ;
const char * error = dlerror ( ) ;
snprintf ( buf , sizeof ( buf ) , " Error loading custom: %s. "
" Make sure this file is in the same directory as the main '4ed' executable. " ,
2017-01-23 06:19:43 +00:00
error ? error : " 'custom_4coder.so' missing " ) ;
2016-09-04 20:41:48 +00:00
LinuxFatalErrorMsg ( buf ) ;
exit ( 1 ) ;
}
# else
linuxvars . custom_api . get_bindings = get_bindings ;
# endif
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// Coroutine / Thread / Semaphore / Mutex init
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . coroutine_free = linuxvars . coroutine_data ;
for ( i32 i = 0 ; i + 1 < ArrayCount ( linuxvars . coroutine_data ) ; + + i ) {
linuxvars . coroutine_data [ i ] . next = linuxvars . coroutine_data + i + 1 ;
}
2017-01-03 20:05:35 +00:00
2017-01-23 06:19:43 +00:00
const size_t stack_size = MB ( 2 ) ;
2016-09-04 20:41:48 +00:00
for ( i32 i = 0 ; i < ArrayCount ( linuxvars . coroutine_data ) ; + + i ) {
linuxvars . coroutine_data [ i ] . stack . ss_size = stack_size ;
2017-03-27 01:52:06 +00:00
linuxvars . coroutine_data [ i ] . stack . ss_sp = system_memory_allocate ( stack_size ) ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Context background [ 4 ] = { } ;
linuxvars . groups [ BACKGROUND_THREADS ] . threads = background ;
linuxvars . groups [ BACKGROUND_THREADS ] . count = ArrayCount ( background ) ;
linuxvars . groups [ BACKGROUND_THREADS ] . cancel_lock0 = CANCEL_LOCK0 ;
linuxvars . groups [ BACKGROUND_THREADS ] . cancel_cv0 = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Memory thread_memory [ ArrayCount ( background ) ] ;
linuxvars . thread_memory = thread_memory ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
sem_init ( & linuxvars . thread_semaphore , 0 , 0 ) ;
linuxvars . queues [ BACKGROUND_THREADS ] . semaphore = LinuxSemToHandle ( & linuxvars . thread_semaphore ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( i32 i = 0 ; i < linuxvars . groups [ BACKGROUND_THREADS ] . count ; + + i ) {
Thread_Context * thread = linuxvars . groups [ BACKGROUND_THREADS ] . threads + i ;
thread - > id = i + 1 ;
thread - > group_id = BACKGROUND_THREADS ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Thread_Memory * memory = linuxvars . thread_memory + i ;
2017-05-22 20:30:53 +00:00
* memory = null_thread_memory ;
2016-09-04 20:41:48 +00:00
memory - > id = thread - > id ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
thread - > queue = & linuxvars . queues [ BACKGROUND_THREADS ] ;
pthread_create ( & thread - > handle , NULL , & JobThreadProc , thread ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
initialize_unbounded_queue ( & linuxvars . groups [ BACKGROUND_THREADS ] . queue ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( i32 i = 0 ; i < LOCK_COUNT ; + + i ) {
pthread_mutex_init ( linuxvars . locks + i , NULL ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( i32 i = 0 ; i < ArrayCount ( linuxvars . conds ) ; + + i ) {
pthread_cond_init ( linuxvars . conds + i , NULL ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// X11 init
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . XDisplay = XOpenDisplay ( 0 ) ;
if ( ! linuxvars . XDisplay ) {
// NOTE(inso): probably not worth trying the popup in this case...
fprintf ( stderr , " Can't open display! \n " ) ;
return 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# define LOAD_ATOM(x) linuxvars.atom_##x = XInternAtom(linuxvars.XDisplay, #x, False);
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LOAD_ATOM ( TARGETS ) ;
LOAD_ATOM ( CLIPBOARD ) ;
LOAD_ATOM ( UTF8_STRING ) ;
LOAD_ATOM ( _NET_WM_STATE ) ;
LOAD_ATOM ( _NET_WM_STATE_MAXIMIZED_HORZ ) ;
LOAD_ATOM ( _NET_WM_STATE_MAXIMIZED_VERT ) ;
LOAD_ATOM ( _NET_WM_STATE_FULLSCREEN ) ;
LOAD_ATOM ( _NET_WM_PING ) ;
LOAD_ATOM ( _NET_WM_WINDOW_TYPE ) ;
LOAD_ATOM ( _NET_WM_WINDOW_TYPE_NORMAL ) ;
LOAD_ATOM ( _NET_WM_PID ) ;
LOAD_ATOM ( WM_DELETE_WINDOW ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
# undef LOAD_ATOM
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . dpi_x = linuxvars . dpi_y = LinuxGetXSettingsDPI ( linuxvars . XDisplay , DefaultScreen ( linuxvars . XDisplay ) ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
// fallback
if ( linuxvars . dpi_x = = - 1 ) {
int scr = DefaultScreen ( linuxvars . XDisplay ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int dw = DisplayWidth ( linuxvars . XDisplay , scr ) ;
int dh = DisplayHeight ( linuxvars . XDisplay , scr ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int dw_mm = DisplayWidthMM ( linuxvars . XDisplay , scr ) ;
int dh_mm = DisplayHeightMM ( linuxvars . XDisplay , scr ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . dpi_x = dw_mm ? dw / ( dw_mm / 25.4 ) : 96 ;
linuxvars . dpi_y = dh_mm ? dh / ( dh_mm / 25.4 ) : 96 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " %dx%d - %dmmx%dmm DPI: %dx%d \n " , dw , dh , dw_mm , dh_mm , linuxvars . dpi_x , linuxvars . dpi_y ) ;
2017-03-30 06:40:16 +00:00
}
else {
2016-09-04 20:41:48 +00:00
fprintf ( stderr , " DPI from XSETTINGS: %d \n " , linuxvars . dpi_x ) ;
}
2017-01-03 20:05:35 +00:00
2017-05-18 21:28:33 +00:00
int window_width , window_height ;
if ( ! LinuxX11WindowInit ( argc , argv , & window_width , & window_height ) ) {
2016-09-04 20:41:48 +00:00
return 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
int xfixes_version_unused , xfixes_err_unused ;
2017-03-30 06:40:16 +00:00
b32 xquery_extension_r = XQueryExtension ( linuxvars . XDisplay , " XFIXES " , & xfixes_version_unused , & linuxvars . xfixes_selection_event , & xfixes_err_unused ) ;
linuxvars . has_xfixes = ( xquery_extension_r = = True ) ;
if ( linuxvars . has_xfixes ) {
XFixesSelectSelectionInput ( linuxvars . XDisplay , linuxvars . XWindow , linuxvars . atom_CLIPBOARD , XFixesSetSelectionOwnerNotifyMask ) ;
}
else {
fprintf ( stderr , " Your X server doesn't support XFIXES, mention this fact if you report any clipboard-related issues. \n " ) ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
Init_Input_Result input_result = LinuxInputInit ( linuxvars . XDisplay , linuxvars . XWindow ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . input_method = input_result . input_method ;
linuxvars . input_style = input_result . best_style ;
linuxvars . input_context = input_result . xic ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LinuxKeycodeInit ( linuxvars . XDisplay ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Cursor xcursors [ APP_MOUSE_CURSOR_COUNT ] = {
None ,
2016-09-10 00:10:43 +00:00
None ,
2016-09-04 20:41:48 +00:00
XCreateFontCursor ( linuxvars . XDisplay , XC_xterm ) ,
XCreateFontCursor ( linuxvars . XDisplay , XC_sb_h_double_arrow ) ,
XCreateFontCursor ( linuxvars . XDisplay , XC_sb_v_double_arrow )
} ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
{
char data = 0 ;
XColor c = { } ;
Pixmap p = XCreateBitmapFromData ( linuxvars . XDisplay , linuxvars . XWindow , & data , 1 , 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . hidden_cursor = XCreatePixmapCursor ( linuxvars . XDisplay , p , p , & c , & c , 0 , 0 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XFreePixmap ( linuxvars . XDisplay , p ) ;
}
2017-01-03 20:05:35 +00:00
2017-03-19 18:25:12 +00:00
//
// Font System Init
//
2017-03-29 16:32:06 +00:00
system_font_init ( & linuxvars . system . font , 0 , 0 , linuxvars . settings . font_size , linuxvars . settings . use_hinting ) ;
2017-03-19 18:25:12 +00:00
2016-09-04 20:41:48 +00:00
//
// Epoll init
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . x11_fd = ConnectionNumber ( linuxvars . XDisplay ) ;
linuxvars . inotify_fd = inotify_init1 ( IN_NONBLOCK ) ;
linuxvars . step_event_fd = eventfd ( 0 , EFD_NONBLOCK ) ;
linuxvars . step_timer_fd = timerfd_create ( CLOCK_MONOTONIC , TFD_NONBLOCK ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . epoll = epoll_create ( 16 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
{
struct epoll_event e = { } ;
e . events = EPOLLIN | EPOLLET ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
e . data . u64 = LINUX_4ED_EVENT_X11 ;
epoll_ctl ( linuxvars . epoll , EPOLL_CTL_ADD , linuxvars . x11_fd , & e ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
e . data . u64 = LINUX_4ED_EVENT_STEP ;
epoll_ctl ( linuxvars . epoll , EPOLL_CTL_ADD , linuxvars . step_event_fd , & e ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
e . data . u64 = LINUX_4ED_EVENT_STEP_TIMER ;
epoll_ctl ( linuxvars . epoll , EPOLL_CTL_ADD , linuxvars . step_timer_fd , & e ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// App init
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
XAddConnectionWatch ( linuxvars . XDisplay , & LinuxX11ConnectionWatch , NULL ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
linuxvars . app . init ( & linuxvars . system , & linuxvars . target , & memory_vars , linuxvars . clipboard_contents , current_directory , linuxvars . custom_api ) ;
2017-01-03 20:05:35 +00:00
2017-05-18 21:28:33 +00:00
LinuxResizeTarget ( window_width , window_height ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
//
// Main loop
//
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( FRAME_LOCK ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LinuxScheduleStep ( ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . keep_running = 1 ;
linuxvars . input . first_step = 1 ;
linuxvars . input . dt = ( frame_useconds / 1000000.f ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
while ( 1 ) {
if ( XEventsQueued ( linuxvars . XDisplay , QueuedAlready ) ) {
2016-09-04 20:41:48 +00:00
LinuxHandleX11Events ( ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_release_lock ( FRAME_LOCK ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
struct epoll_event events [ 16 ] ;
int num_events = epoll_wait ( linuxvars . epoll , events , ArrayCount ( events ) , - 1 ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
system_acquire_lock ( FRAME_LOCK ) ;
2017-01-03 20:05:35 +00:00
2017-03-30 06:40:16 +00:00
if ( num_events = = - 1 ) {
if ( errno ! = EINTR ) {
fprintf ( stderr , " epoll_wait \n " ) ;
2016-09-04 20:41:48 +00:00
}
continue ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
b32 do_step = 0 ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
for ( int i = 0 ; i < num_events ; + + i ) {
int fd = events [ i ] . data . u64 & UINT32_MAX ;
u64 type = events [ i ] . data . u64 & ~ fd ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
switch ( type ) {
case LINUX_4ED_EVENT_X11 : {
LinuxHandleX11Events ( ) ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case LINUX_4ED_EVENT_X11_INTERNAL : {
XProcessInternalConnection ( linuxvars . XDisplay , fd ) ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case LINUX_4ED_EVENT_STEP : {
u64 ev ;
int ret ;
do {
ret = read ( linuxvars . step_event_fd , & ev , 8 ) ;
} while ( ret ! = - 1 | | errno ! = EAGAIN ) ;
do_step = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case LINUX_4ED_EVENT_STEP_TIMER : {
u64 count ;
int ret ;
do {
ret = read ( linuxvars . step_timer_fd , & count , 8 ) ;
} while ( ret ! = - 1 | | errno ! = EAGAIN ) ;
do_step = 1 ;
} break ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
case LINUX_4ED_EVENT_CLI : {
LinuxScheduleStep ( ) ;
} break ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( do_step ) {
linuxvars . last_step = system_now_time ( ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . input . first_step | | ! linuxvars . has_xfixes ) {
XConvertSelection (
linuxvars . XDisplay ,
linuxvars . atom_CLIPBOARD ,
linuxvars . atom_UTF8_STRING ,
linuxvars . atom_CLIPBOARD ,
linuxvars . XWindow ,
CurrentTime
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
Application_Step_Result result = { } ;
result . mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT ;
result . trying_to_kill = ! linuxvars . keep_running ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( linuxvars . new_clipboard ) {
linuxvars . input . clipboard = linuxvars . clipboard_contents ;
linuxvars . new_clipboard = 0 ;
} else {
linuxvars . input . clipboard = null_string ;
}
2017-01-03 20:05:35 +00:00
2016-09-10 02:57:45 +00:00
b32 keep_running = linuxvars . keep_running ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . app . step (
& linuxvars . system ,
& linuxvars . target ,
& memory_vars ,
& linuxvars . input ,
2016-09-20 18:10:05 +00:00
& result ,
clparams
2017-01-03 20:05:35 +00:00
) ;
2016-09-04 20:41:48 +00:00
if ( result . perform_kill ) {
break ;
2016-09-10 02:57:45 +00:00
} else if ( ! keep_running & & ! linuxvars . keep_running ) {
2016-09-04 20:41:48 +00:00
linuxvars . keep_running = 1 ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( result . animating ) {
LinuxScheduleStep ( ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
LinuxRedrawTarget ( ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
if ( result . mouse_cursor_type ! = linuxvars . cursor & & ! linuxvars . input . mouse . l ) {
Cursor c = xcursors [ result . mouse_cursor_type ] ;
if ( ! linuxvars . hide_cursor ) {
XDefineCursor ( linuxvars . XDisplay , linuxvars . XWindow , c ) ;
}
linuxvars . cursor = result . mouse_cursor_type ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
flush_thread_group ( BACKGROUND_THREADS ) ;
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
linuxvars . input . first_step = 0 ;
2016-09-09 16:47:07 +00:00
linuxvars . input . keys = null_key_input_data ;
2016-09-04 20:41:48 +00:00
linuxvars . input . mouse . press_l = 0 ;
linuxvars . input . mouse . release_l = 0 ;
linuxvars . input . mouse . press_r = 0 ;
linuxvars . input . mouse . release_r = 0 ;
linuxvars . input . mouse . wheel = 0 ;
}
}
2017-01-03 20:05:35 +00:00
2016-09-05 01:42:22 +00:00
if ( linuxvars . XDisplay ) {
if ( linuxvars . XWindow ) XDestroyWindow ( linuxvars . XDisplay , linuxvars . XWindow ) ;
XCloseDisplay ( linuxvars . XDisplay ) ;
}
2017-01-03 20:05:35 +00:00
2016-09-04 20:41:48 +00:00
return 0 ;
}
2017-03-27 01:52:06 +00:00
# include "linux_4ed_fonts.cpp"
# include "linux_4ed_file_track.cpp"
# include "4ed_font_static_functions.cpp"
2017-03-19 18:25:12 +00:00
2016-09-04 20:41:48 +00:00
// BOTTOM
// vim: expandtab:ts=4:sts=4:sw=4