diff --git a/platform_linux/linux_4ed_functions.cpp b/platform_linux/linux_4ed_functions.cpp index 2cebb4d0..db0e400d 100644 --- a/platform_linux/linux_4ed_functions.cpp +++ b/platform_linux/linux_4ed_functions.cpp @@ -11,7 +11,7 @@ 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 @@ -19,70 +19,70 @@ system_get_path(Arena* arena, System_Path_Code path_code){ 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)); } 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++; } @@ -91,13 +91,13 @@ system_get_canonical(Arena* arena, String_Const_u8 name){ *q++ = *p++; } } - + #ifdef INSO_DEBUG if(name.size != q - output) { LINUX_FN_DEBUG("[%.*s] -> [%.*s]", (int)name.size, name.str, (int)(q - output), output); } #endif - + // TODO: use realpath at this point to resolve symlinks? return SCu8(output, q - output); } @@ -106,57 +106,57 @@ internal File_List system_get_file_list(Arena* arena, String_Const_u8 directory){ //LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str); File_List result = {}; - + char* path = strndupa((char*)directory.str, directory.size); int fd = open(path, O_RDONLY | O_DIRECTORY); if(fd == -1) { perror("open"); return result; } - + DIR* dir = fdopendir(fd); struct dirent* d; - + File_Info* head = NULL; File_Info** fip = &head; - + while((d = readdir(dir))) { const char* name = d->d_name; - + // ignore . and .. if(*name == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) { continue; } - + *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++; } closedir(dir); - + if(result.count > 0) { result.infos = fip = push_array(arena, File_Info*, result.count); - + for(File_Info* f = head; f; f = f->next) { *fip++ = f; } - + qsort(result.infos, result.count, sizeof(File_Info*), (__compar_fn_t)&linux_compare_file_infos); - + for(u32 i = 0; i < result.count - 1; ++i) { result.infos[i]->next = result.infos[i+1]; } result.infos[result.count-1]->next = NULL; } - + return result; } @@ -209,9 +209,9 @@ internal File_Attributes system_save_file(Arena* scratch, char* file_name, String_Const_u8 data){ LINUX_FN_DEBUG("%s", file_name); File_Attributes result = {}; - + // TODO(inso): should probably put a \n on the end if it's a text file. - + int fd = open(file_name, O_WRONLY, O_CREAT); if (fd != -1) { int bytes_written = write(fd, data.str, data.size); @@ -225,7 +225,7 @@ system_save_file(Arena* scratch, char* file_name, String_Const_u8 data){ } else { perror("open"); } - + return result; } @@ -265,7 +265,7 @@ system_wake_up_timer_create(void){ LINUX_FN_DEBUG(); 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; } @@ -287,23 +287,23 @@ internal void system_wake_up_timer_set(Plat_Handle handle, u32 time_milliseconds){ //LINUX_FN_DEBUG("%u", time_milliseconds); 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); } - + struct itimerspec it = {}; it.it_value.tv_sec = time_milliseconds / 1000; it.it_value.tv_nsec = (time_milliseconds % 1000) * UINT64_C(1000000); timerfd_settime(object->timer.fd, 0, &it, NULL); } - + } internal void @@ -358,28 +358,28 @@ system_cli_call(Arena* scratch, char* path, char* script, CLI_Handles* cli_out){ 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"); } @@ -387,17 +387,17 @@ system_cli_call(Arena* scratch, char* path, char* script, CLI_Handles* cli_out){ } 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); } @@ -411,16 +411,16 @@ internal b32 system_cli_update_step(CLI_Handles* cli, char* dest, u32 max, u32* amount){ LINUX_FN_DEBUG(); 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){ @@ -433,7 +433,7 @@ system_cli_update_step(CLI_Handles* cli, char* dest, u32 max, u32* amount){ space_left -= num; } } - + *amount = (ptr - dest); return((ptr - dest) > 0); } @@ -443,16 +443,16 @@ system_cli_end_update(CLI_Handles* cli){ LINUX_FN_DEBUG(); 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); } - + return(close_me); } @@ -484,28 +484,28 @@ internal System_Thread system_thread_launch(Thread_Function* proc, void* ptr){ LINUX_FN_DEBUG(); 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); - + &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)); + static_assert(sizeof(Linux_Object*) <= sizeof(System_Thread), "Linux_Object doesn't fit inside System_Thread"); *(Linux_Object**)&result = thread_info; return result; } - + return result; } @@ -626,31 +626,31 @@ system_condition_variable_free(System_Condition_Variable cv){ internal void* system_memory_allocate(u64 size, String_Const_u8 location){ - - static_assert(MEMORY_PREFIX_SIZE >= sizeof(Memory_Annotation_Node)); + + static_assert(MEMORY_PREFIX_SIZE >= sizeof(Memory_Annotation_Node), "MEMORY_PREFIX_SIZE is not enough to contain Memory_Annotation_Node"); u64 adjusted_size = size + MEMORY_PREFIX_SIZE; - + Assert(adjusted_size > size); - + const int prot = PROT_READ | PROT_WRITE; const int flags = MAP_PRIVATE | MAP_ANONYMOUS; - + void* result = mmap(NULL, adjusted_size, prot, flags, -1, 0); - + if(result == MAP_FAILED) { perror("mmap"); return NULL; } - + Linux_Memory_Tracker_Node* node = (Linux_Memory_Tracker_Node*)result; node->location = location; node->size = size; - + pthread_mutex_lock(&linuxvars.memory_tracker_mutex); zdll_push_back(linuxvars.memory_tracker_head, linuxvars.memory_tracker_tail, node); linuxvars.memory_tracker_count++; pthread_mutex_unlock(&linuxvars.memory_tracker_mutex); - + return (u8*)result + MEMORY_PREFIX_SIZE; } @@ -669,12 +669,12 @@ internal void system_memory_free(void* ptr, u64 size){ u64 adjusted_size = size + MEMORY_PREFIX_SIZE; Linux_Memory_Tracker_Node* node = (Linux_Memory_Tracker_Node*)((u8*)ptr - MEMORY_PREFIX_SIZE); - + pthread_mutex_lock(&linuxvars.memory_tracker_mutex); zdll_remove(linuxvars.memory_tracker_head, linuxvars.memory_tracker_tail, node); linuxvars.memory_tracker_count--; pthread_mutex_unlock(&linuxvars.memory_tracker_mutex); - + if(munmap(node, adjusted_size) == -1) { perror("munmap"); } @@ -683,12 +683,12 @@ system_memory_free(void* ptr, u64 size){ internal Memory_Annotation system_memory_annotation(Arena* arena){ LINUX_FN_DEBUG(); - + Memory_Annotation result; Memory_Annotation_Node** ptr = &result.first; - + pthread_mutex_lock(&linuxvars.memory_tracker_mutex); - + for(Linux_Memory_Tracker_Node* node = linuxvars.memory_tracker_head; node; node = node->next) { *ptr = push_array(arena, Memory_Annotation_Node, 1); (*ptr)->location = node->location; @@ -697,25 +697,25 @@ system_memory_annotation(Arena* arena){ ptr = &(*ptr)->next; result.count++; } - + pthread_mutex_unlock(&linuxvars.memory_tracker_mutex); - + *ptr = NULL; result.last = CastFromMember(Memory_Annotation_Node, next, ptr); - + return result; } internal void system_show_mouse_cursor(i32 show){ LINUX_FN_DEBUG("%d", show); - + linuxvars.cursor_show = show; - + XDefineCursor( - linuxvars.dpy, - linuxvars.win, - show ? None : linuxvars.hidden_cursor); + linuxvars.dpy, + linuxvars.win, + show ? None : linuxvars.hidden_cursor); } internal b32 @@ -727,11 +727,11 @@ system_set_fullscreen(b32 full_screen){ 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; @@ -741,12 +741,12 @@ system_is_fullscreen(void){ 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; }