4coder/platform_linux/linux_4ed_functions.cpp

691 lines
19 KiB
C++
Raw Normal View History

2020-02-02 15:50:18 +00:00
/*
* chr - Andrew Chronister &
* inso - Alex Baines
*
* 02.02.2020
*
* Updated linux layer for 4coder
*
*/
internal String_Const_u8
system_get_path(Arena* arena, System_Path_Code path_code){
String_Const_u8 result = {};
switch (path_code){
case SystemPath_CurrentDirectory: {
// glibc extension: getcwd allocates its own memory if passed NULL
char *working_dir = getcwd(NULL, 0);
LINUX_FN_DEBUG("cwd = [%s]", working_dir);
u64 working_dir_len = cstring_length(working_dir);
u8 *out = push_array(arena, u8, working_dir_len + 1);
block_copy(out, working_dir, working_dir_len);
// NOTE: 4ed appears to expect a slash on the end.
out[working_dir_len] = '/';
free(working_dir);
result = SCu8(out, working_dir_len + 1);
} break;
case SystemPath_Binary: {
// linux-specific: binary path symlinked at /proc/self/exe
// PATH_MAX is probably good enough...
// read the 'readlink' manpage for some comedy about it being 'broken by design'.
char* buf = push_array(arena, char, PATH_MAX);
ssize_t n = readlink("/proc/self/exe", buf, PATH_MAX);
if(n == -1) {
perror("readlink");
*buf = n = 0;
}
result = string_remove_last_folder(SCu8(buf, n));
LINUX_FN_DEBUG("bin dir = [%.*s]", (int)result.size, result.str);
} break;
}
return(result);
}
internal String_Const_u8
system_get_canonical(Arena* arena, String_Const_u8 name){
// first remove redundant ../, //, ./ parts
const u8* input = (u8*) strndupa((char*)name.str, name.size);
u8* output = push_array(arena, u8, name.size + 1);
const u8* p = input;
u8* q = output;
while(*p) {
// not a slash - copy char
if(p[0] != '/') {
*q++ = *p++;
continue;
}
// two slashes in a row, skip one.
if(p[1] == '/') {
++p;
}
else if(p[1] == '.') {
// skip "/./" or trailing "/."
if(p[2] == '/' || p[2] == '\0') {
p += 2;
}
// if we encounter "/../" or trailing "/..", remove last directory instead
else if(p[2] == '.' && (p[3] == '/' || p[3] == '\0')) {
while(q > output && *--q != '/'){};
p += 3;
}
else {
*q++ = *p++;
}
}
else {
*q++ = *p++;
}
}
LINUX_FN_DEBUG("[%.*s] -> [%.*s]", (int)name.size, name.str, (int)(q - output), output);
// TODO: use realpath at this point to resolve symlinks?
return SCu8(output, q - output);
2020-02-02 15:50:18 +00:00
}
internal File_List
system_get_file_list(Arena* arena, String_Const_u8 directory){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str);
2020-02-02 15:50:18 +00:00
File_List result = {};
2020-02-04 21:38:37 +00:00
char* path = strndupa((char*)directory.str, directory.size);
int fd = open(path, O_RDONLY | O_DIRECTORY);
if(fd == -1) {
perror("open");
return result;
2020-02-02 15:50:18 +00:00
}
2020-02-04 21:38:37 +00:00
DIR* dir = fdopendir(fd);
struct dirent* d;
File_Info* head = NULL;
File_Info** fip = &head;
2020-02-02 15:50:18 +00:00
2020-02-04 21:38:37 +00:00
while((d = readdir(dir))) {
const char* name = d->d_name;
2020-02-02 15:50:18 +00:00
2020-02-04 21:38:37 +00:00
// ignore . and ..
if(*name == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) {
continue;
}
2020-02-02 15:50:18 +00:00
2020-02-04 21:38:37 +00:00
*fip = push_array(arena, File_Info, 1);
(*fip)->file_name = push_u8_stringf(arena, "%.*s", d->d_reclen, name);
struct stat st;
if(fstatat(fd, name, &st, 0) == -1){
perror("fstatat");
}
(*fip)->attributes = linux_file_attributes_from_struct_stat(&st);
fip = &(*fip)->next;
result.count++;
2020-02-02 15:50:18 +00:00
}
2020-02-04 21:38:37 +00:00
closedir(dir);
2020-02-02 15:50:18 +00:00
if(result.count > 0) {
result.infos = fip = push_array(arena, File_Info*, result.count);
2020-02-02 15:50:18 +00:00
for(File_Info* f = head; f; f = f->next) {
*fip++ = f;
}
2020-02-02 15:50:18 +00:00
// NOTE(inso): I want to sort them like this (. files lower), but it looks like
// the sorting is done on the custom-layer side (lister), so this is pointless.
// TODO(inso): add linux-specific custom layer code?
2020-02-04 21:38:37 +00:00
/*
qsort(result.infos, result.count, sizeof(File_Info*), (__compar_fn_t)&linux_compare_file_infos);
2020-02-04 21:38:37 +00:00
for(u32 i = 0; i < result.count - 1; ++i) {
result.infos[i]->next = result.infos[i+1];
}
result.infos[result.count-1]->next = NULL;
*/
}
2020-02-04 21:38:37 +00:00
2020-02-02 15:50:18 +00:00
return result;
}
internal File_Attributes
system_quick_file_attributes(Arena* scratch, String_Const_u8 file_name){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%.*s", (int)file_name.size, file_name.str);
2020-02-02 15:50:18 +00:00
struct stat file_stat;
stat((const char*)file_name.str, &file_stat);
2020-02-04 21:38:37 +00:00
return linux_file_attributes_from_struct_stat(&file_stat);
2020-02-02 15:50:18 +00:00
}
internal b32
system_load_handle(Arena* scratch, char* file_name, Plat_Handle* out){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%s", file_name);
2020-02-02 15:50:18 +00:00
int fd = open(file_name, O_RDONLY);
if (fd != -1) {
*(int*)out = fd;
return true;
}
return false;
}
internal File_Attributes
system_load_attributes(Plat_Handle handle){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
struct stat file_stat;
fstat(*(int*)&handle, &file_stat);
2020-02-04 21:38:37 +00:00
return linux_file_attributes_from_struct_stat(&file_stat);
2020-02-02 15:50:18 +00:00
}
internal b32
system_load_file(Plat_Handle handle, char* buffer, u32 size){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%.*s", size, buffer);
2020-02-02 15:50:18 +00:00
int fd = *(int*)&handle;
int bytes_read = read(fd, buffer, size);
if (bytes_read == size) {
return true;
}
return false;
}
internal b32
system_load_close(Plat_Handle handle){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
int fd = *(int*)&handle;
return close(fd) == 0;
}
internal File_Attributes
system_save_file(Arena* scratch, char* file_name, String_Const_u8 data){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%s", file_name);
2020-02-02 15:50:18 +00:00
File_Attributes result = {};
int fd = open(file_name, O_WRONLY, O_CREAT);
if (fd != -1) {
int bytes_written = write(fd, data.str, data.size);
if (bytes_written != -1) {
struct stat file_stat;
fstat(fd, &file_stat);
2020-02-04 21:38:37 +00:00
return linux_file_attributes_from_struct_stat(&file_stat);
2020-02-02 15:50:18 +00:00
}
}
return result;
}
internal b32
system_load_library(Arena* scratch, String_Const_u8 file_name, System_Library* out){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%.*s", (int)file_name.size, file_name.str);
2020-02-02 15:50:18 +00:00
void* library = dlopen((const char*)file_name.str, RTLD_LAZY);
if (library != NULL) {
*(void**)out = library;
return true;
}
return false;
}
internal b32
system_release_library(System_Library handle){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
return dlclose(*(void**)&handle) == 0;
}
internal Void_Func*
system_get_proc(System_Library handle, char* proc_name){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%s", proc_name);
2020-02-02 15:50:18 +00:00
return (Void_Func*)dlsym(*(void**)&handle, proc_name);
}
internal u64
system_now_time(void){
//LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
struct timespec time;
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
return linux_u64_from_timespec(time);
}
internal Plat_Handle
system_wake_up_timer_create(void){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
Linux_Object* object = linux_alloc_object(LinuxObjectKind_Timer);
dll_insert(&linuxvars.timer_objects, &object->node);
// NOTE(inso): timers created on-demand to avoid file-descriptor exhaustion.
object->timer.fd = -1;
}
internal void
system_wake_up_timer_release(Plat_Handle handle){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
Linux_Object* object = handle_to_object(handle);
if (object->kind == LinuxObjectKind_Timer){
if(object->timer.fd != -1) {
epoll_ctl(linuxvars.epoll, EPOLL_CTL_DEL, object->timer.fd, NULL);
close(object->timer.fd);
object->timer.fd = -1;
}
linux_free_object(object);
}
}
internal void
system_wake_up_timer_set(Plat_Handle handle, u32 time_milliseconds){
LINUX_FN_DEBUG("%u", time_milliseconds);
2020-02-02 15:50:18 +00:00
Linux_Object* object = handle_to_object(handle);
if (object->kind == LinuxObjectKind_Timer){
if(object->timer.fd == -1) {
object->timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
ev.data.ptr = &object->timer.epoll_tag;
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, object->timer.fd, &ev);
2020-02-02 15:50:18 +00:00
}
struct itimerspec it = {};
it.it_value.tv_sec = time_milliseconds / 1000;
2020-02-03 21:18:46 +00:00
it.it_value.tv_nsec = (time_milliseconds % 1000) * UINT64_C(1000000);
timerfd_settime(object->timer.fd, 0, &it, NULL);
2020-02-02 15:50:18 +00:00
}
}
internal void
system_signal_step(u32 code){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%d", code);
2020-02-02 15:50:18 +00:00
linux_schedule_step();
}
internal void
system_sleep(u64 microseconds){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%" PRIu64, microseconds);
2020-02-02 15:50:18 +00:00
struct timespec requested;
struct timespec remaining;
u64 seconds = microseconds / Million(1);
requested.tv_sec = seconds;
requested.tv_nsec = (microseconds - seconds * Million(1)) * Thousand(1);
nanosleep(&requested, &remaining);
}
internal void
system_post_clipboard(String_Const_u8 str){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%.*s", (int)str.size, str.str);
2020-02-02 15:50:18 +00:00
linalloc_clear(&linuxvars.clipboard_out_arena);
char* p = push_array(&linuxvars.clipboard_out_arena, char, str.size + 1);
block_copy(p, str.data, str.size);
p[str.size] = '\0';
XSetSelectionOwner(linuxvars.dpy, linuxvars.atom_CLIPBOARD, linuxvars.win, CurrentTime);
}
internal b32
system_cli_call(Arena* scratch, char* path, char* script, CLI_Handles* cli_out){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%s / %s", path, script);
2020-02-02 15:50:18 +00:00
int pipe_fds[2];
if (pipe(pipe_fds) == -1){
perror("system_cli_call: pipe");
return 0;
}
pid_t child_pid = vfork();
if (child_pid == -1){
perror("system_cli_call: fork");
return 0;
}
enum { PIPE_FD_READ, PIPE_FD_WRITE };
// 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);
if (chdir(path) == -1){
perror("system_cli_call: chdir");
exit(1);
}
char* argv[] = { "sh", "-c", script, NULL };
if (execv("/bin/sh", argv) == -1){
perror("system_cli_call: execv");
}
exit(1);
}
else{
close(pipe_fds[PIPE_FD_WRITE]);
*(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];
struct epoll_event e = {};
e.events = EPOLLIN | EPOLLET;
e.data.ptr = &epoll_tag_cli_pipe;
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, pipe_fds[PIPE_FD_READ], &e);
}
return(true);
}
internal void
system_cli_begin_update(CLI_Handles* cli){
// NOTE(inso): I don't think anything needs to be done here.
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
}
internal b32
system_cli_update_step(CLI_Handles* cli, char* dest, u32 max, u32* amount){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
int pipe_read_fd = *(int*)&cli->out_read;
fd_set fds;
FD_ZERO(&fds);
FD_SET(pipe_read_fd, &fds);
struct timeval tv = {};
size_t space_left = max;
char* ptr = dest;
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;
}
}
*amount = (ptr - dest);
return((ptr - dest) > 0);
}
internal b32
system_cli_end_update(CLI_Handles* cli){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
pid_t pid = *(pid_t*)&cli->proc;
b32 close_me = false;
int status;
if (pid && waitpid(pid, &status, WNOHANG) > 0){
cli->exit = WEXITSTATUS(status);
close_me = true;
close(*(int*)&cli->out_read);
close(*(int*)&cli->out_write);
struct epoll_event e = {};
epoll_ctl(linuxvars.epoll, EPOLL_CTL_DEL, *(int*)&cli->out_read, &e);
}
return(close_me);
}
internal void
system_open_color_picker(Color_Picker* picker){
// TODO?
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
}
internal f32
system_get_screen_scale_factor(void){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
// TODO: correct screen number somehow
int dpi = linux_get_xsettings_dpi(linuxvars.dpy, 0);
if(dpi == -1){
int scr = DefaultScreen(linuxvars.dpy);
int dw = DisplayWidth(linuxvars.dpy, scr);
int dh = DisplayHeight(linuxvars.dpy, scr);
int dw_mm = DisplayWidthMM(linuxvars.dpy, scr);
int dh_mm = DisplayHeightMM(linuxvars.dpy, scr);
int dpi_x = dw_mm ? dw / (dw_mm / 25.4) : 96;
int dpi_y = dh_mm ? dh / (dh_mm / 25.4) : 96;
dpi = dpi_x > dpi_y ? dpi_x : dpi_y;
}
return dpi / 96.0f;
}
internal System_Thread
system_thread_launch(Thread_Function* proc, void* ptr){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
System_Thread result = {};
Linux_Object* thread_info = linux_alloc_object(LinuxObjectKind_Thread);
thread_info->thread.proc = proc;
thread_info->thread.ptr = ptr;
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
int create_result = pthread_create(
&thread_info->thread.pthread,
&thread_attr,
linux_thread_proc_start,
thread_info);
pthread_attr_destroy(&thread_attr);
// TODO(andrew): Need to wait for thread to confirm it launched?
if (create_result == 0) {
static_assert(sizeof(Linux_Object*) <= sizeof(System_Thread));
*(Linux_Object**)&result = thread_info;
return result;
}
return result;
}
internal void
system_thread_join(System_Thread thread){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
Linux_Object* object = *(Linux_Object**)&thread;
void* retval_ignored;
int result = pthread_join(object->thread.pthread, &retval_ignored);
}
internal void
system_thread_free(System_Thread thread){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
Linux_Object* object = *(Linux_Object**)&thread;
Assert(object->kind == LinuxObjectKind_Thread);
linux_free_object(object);
}
internal i32
system_thread_get_id(void){
2020-02-03 21:18:46 +00:00
pid_t id = syscall(__NR_gettid);
//LINUX_FN_DEBUG("%d", id);
2020-02-03 21:18:46 +00:00
return id;
2020-02-02 15:50:18 +00:00
}
internal void
system_acquire_global_frame_mutex(Thread_Context* tctx){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
if (tctx->kind == ThreadKind_AsyncTasks){
system_mutex_acquire(linuxvars.global_frame_mutex);
}
}
internal void
system_release_global_frame_mutex(Thread_Context* tctx){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
if (tctx->kind == ThreadKind_AsyncTasks){
system_mutex_release(linuxvars.global_frame_mutex);
}
}
internal System_Mutex
system_mutex_make(void){
System_Mutex result = {};
Linux_Object* object = linux_alloc_object(LinuxObjectKind_Mutex);
pthread_mutex_init(&object->mutex, NULL);
*(Linux_Object**)&result = object;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
return result;
}
internal void
system_mutex_acquire(System_Mutex mutex){
Linux_Object* object = *(Linux_Object**)&mutex;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
Assert(object->kind == LinuxObjectKind_Mutex);
pthread_mutex_lock(&object->mutex);
}
internal void
system_mutex_release(System_Mutex mutex){
Linux_Object* object = *(Linux_Object**)&mutex;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
Assert(object->kind == LinuxObjectKind_Mutex);
pthread_mutex_unlock(&object->mutex);
}
internal void
system_mutex_free(System_Mutex mutex){
Linux_Object* object = *(Linux_Object**)&mutex;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
Assert(object->kind == LinuxObjectKind_Mutex);
pthread_mutex_destroy(&object->mutex);
linux_free_object(object);
}
internal System_Condition_Variable
system_condition_variable_make(void){
System_Condition_Variable result = {};
Linux_Object* object = linux_alloc_object(LinuxObjectKind_ConditionVariable);
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
pthread_cond_init(&object->condition_variable, NULL);
*(Linux_Object**)&result = object;
return result;
}
internal void
system_condition_variable_wait(System_Condition_Variable cv, System_Mutex mutex){
Linux_Object* cv_object = *(Linux_Object**)&cv;
Linux_Object* mutex_object = *(Linux_Object**)&mutex;
//LINUX_FN_DEBUG("%p / %p", cv_object, mutex_object);
2020-02-02 15:50:18 +00:00
Assert(cv_object->kind == LinuxObjectKind_ConditionVariable);
Assert(mutex_object->kind == LinuxObjectKind_Mutex);
pthread_cond_wait(&cv_object->condition_variable, &mutex_object->mutex);
}
internal void
system_condition_variable_signal(System_Condition_Variable cv){
Linux_Object* object = *(Linux_Object**)&cv;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
Assert(object->kind == LinuxObjectKind_ConditionVariable);
pthread_cond_signal(&object->condition_variable);
}
internal void
system_condition_variable_free(System_Condition_Variable cv){
Linux_Object* object = *(Linux_Object**)&cv;
//LINUX_FN_DEBUG("%p", object);
2020-02-02 15:50:18 +00:00
Assert(object->kind == LinuxObjectKind_ConditionVariable);
pthread_cond_destroy(&object->condition_variable);
linux_free_object(object);
}
internal void*
system_memory_allocate(u64 size, String_Const_u8 location){
2020-02-02 20:20:27 +00:00
2020-02-02 15:50:18 +00:00
void* result = mmap(
NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// TODO(andrew): Allocation tracking?
//LINUX_FN_DEBUG("%" PRIu64 ", %.*s %p", size, (int)location.size, location.str, result);
2020-02-02 15:50:18 +00:00
return result;
}
internal b32
system_memory_set_protection(void* ptr, u64 size, u32 flags){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%p / %ld / %d", ptr, size, flags);
2020-02-02 15:50:18 +00:00
int protect = 0;
MovFlag(flags, MemProtect_Read, protect, PROT_READ);
MovFlag(flags, MemProtect_Write, protect, PROT_WRITE);
MovFlag(flags, MemProtect_Execute, protect, PROT_EXEC);
int result = mprotect(ptr, size, protect);
return result == 0;
}
internal void
system_memory_free(void* ptr, u64 size){
//LINUX_FN_DEBUG("%p / %ld", ptr, size);
2020-02-02 15:50:18 +00:00
munmap(ptr, size);
}
internal Memory_Annotation
system_memory_annotation(Arena* arena){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
2020-02-02 15:50:18 +00:00
// TODO;
}
internal void
system_show_mouse_cursor(i32 show){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG("%d", show);
2020-02-02 15:50:18 +00:00
linuxvars.cursor_show = show;
XDefineCursor(
linuxvars.dpy,
linuxvars.win,
show ? None : linuxvars.hidden_cursor);
}
internal b32
system_set_fullscreen(b32 full_screen){
linux_set_wm_state(linuxvars.atom__NET_WM_STATE_FULLSCREEN, 0, full_screen);
2020-02-02 15:50:18 +00:00
return true;
}
internal b32
system_is_fullscreen(void){
b32 result = 0;
// NOTE(inso): This will get the "true" state of fullscreen,
// even if it was toggled outside of 4coder.
// (e.g. super-F11 on some WMs sets fullscreen for any window/program)
Atom type, *prop;
unsigned long nitems, pad;
int fmt;
int ret = XGetWindowProperty(linuxvars.dpy,
linuxvars.win,
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;
XFree((unsigned char*)prop);
}
return result;
2020-02-02 15:50:18 +00:00
}
internal Input_Modifier_Set
system_get_keyboard_modifiers(Arena* arena){
2020-02-02 20:20:27 +00:00
LINUX_FN_DEBUG();
return(copy_modifier_set(arena, &linuxvars.input.pers.modifiers));
2020-02-02 15:50:18 +00:00
}