diff --git a/src/main.c b/src/main.c index daf3dc3f..0024a717 100644 --- a/src/main.c +++ b/src/main.c @@ -7,7 +7,7 @@ #include #elif __linux__ #include - #include + #include "xrdb_parse.h" #elif __APPLE__ #include #endif @@ -15,40 +15,17 @@ SDL_Window *window; - static double get_scale(void) { +#if _WIN32 float dpi; SDL_GetDisplayDPI(0, NULL, &dpi, NULL); -#if _WIN32 return dpi / 96.0; -#elif __linux__ - FILE *stream; - char *xrdb = "xrdb -query | grep dpi | cut -f 2"; - - // In case something goes wrong in popen (e.g., fork or exec calls) or any - // other calls listed below we return the dpi equal to 1. - if ((stream = popen(xrdb, "r")) == NULL) - return 1.0; - - char *line = NULL; - size_t buffer = 0; - ssize_t length = getline(&line, &buffer, stream); - - if (length == -1) { - free(line); - pclose(stream); - return 1.0; +#elif __unix__ + int xft_dpi = xrdb_find_dpi(); + if (xft_dpi > 0) { + return xft_dpi / 96.0; } - - if (pclose(stream)) { - free(line); - return 1.0; - } - - dpi = strtol(line, NULL, 10) / 96.0; - free(line); - - return dpi; + return 1.0; #else return 1.0; #endif diff --git a/src/meson.build b/src/meson.build index 66107d7a..e6e50cc8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,6 +5,7 @@ lite_sources = [ 'api/system.c', 'renderer.c', 'rencache.c', + 'xrdb_parse.c', 'main.c', ] diff --git a/src/xrdb_parse.c b/src/xrdb_parse.c new file mode 100644 index 00000000..47018106 --- /dev/null +++ b/src/xrdb_parse.c @@ -0,0 +1,139 @@ +#ifdef __unix__ + +#include +#include +#include +#include +#include +#include "xrdb_parse.h" + +extern char **environ; + +static int join_path(char buf[], int buf_size, const char *path, const char *name) { + const char *path_delim = strchr(path, ':'); + if (!path_delim) { + path_delim = path + strlen(path); + } + const int path_len = path_delim - path, name_len = strlen(name); + if (buf_size < path_len + 1 + name_len + 1) return -1; + memcpy(buf, path, path_len); + buf[path_len] = '/'; + memcpy(buf + path_len + 1, name, name_len + 1); + return 0; +} + + +static int xrdb_popen (int *pid_ptr) { + int fd[2]; + + if (pipe(fd) != 0) return -1; + int read_fd = fd[0]; + int write_fd = fd[1]; + + int pid = fork(); + if (pid == 0) { + close(read_fd); + dup2(write_fd, STDOUT_FILENO); + close(write_fd); + char *path = getenv("PATH"); + char xrdb_filename[256]; + while (path) { + char *xrgb_argv[3] = {"xrdb", "-query", NULL}; + if (join_path(xrdb_filename, 256, path, "xrdb") == 0) { + execve(xrdb_filename, xrgb_argv, environ); + } + path = strchr(path, ':'); + if (path) { + path++; + } + } + } else { + *pid_ptr = pid; + close(write_fd); + return read_fd; + } + return -1; +} + + +static int xrdb_try_dpi_match(const char *line, int len) { + if (len >= 8 && strncmp(line, "Xft.dpi:", 8) == 0) { + for (line += 8; *line && (*line == '\t' || *line == ' '); line++) { } + int dpi = 0; + for ( ; *line >= '0' && *line <= '9'; line++) { + dpi = dpi * 10 + ((int) (*line) - (int) '0'); + } + return dpi; + } + return -1; +} + +static void xrdb_complete_reading(int read_fd, char *buf, size_t buf_size) { + while (1) { + int nr = read(read_fd, buf, buf_size); + if (nr <= 0) return; + } +} + +#define XRDB_READ_BUF_SIZE 256 +static int xrdb_parse_for_dpi(int read_fd) { + char buf[XRDB_READ_BUF_SIZE]; + char buf_remaining = 0; + while (1) { + int nr = read(read_fd, buf + buf_remaining, XRDB_READ_BUF_SIZE - buf_remaining); + if (nr > 0) { + const char * const buf_limit = buf + nr + buf_remaining; + char *line_start = buf; + while (line_start < buf_limit) { + char *nlptr = line_start; + while (nlptr < buf_limit && *nlptr != '\n') { + nlptr++; + } + if (nlptr < buf_limit) { + int dpi_read = xrdb_try_dpi_match(line_start, nlptr - line_start); + if (dpi_read > 0) { + xrdb_complete_reading(read_fd, buf, XRDB_READ_BUF_SIZE); + return dpi_read; + } + /* doesn't match: position after newline to search again */ + line_start = nlptr + 1; + } else { + /* No newline found: copy the remaining part at the beginning of buf + and read again from file descriptor. */ + buf_remaining = buf_limit - line_start; + if (buf_remaining >= XRDB_READ_BUF_SIZE) { + /* Line is too long for the buffer: skip. */ + buf_remaining = 0; + } else { + memmove(buf, line_start, buf_remaining); + } + break; + } + } + } else { + if (nr == 0 && buf_remaining > 0) { + int dpi_read = xrdb_try_dpi_match(buf, buf_remaining); + if (dpi_read > 0) { + return dpi_read; + } + } + break; + } + } + return -1; +} + +int xrdb_find_dpi() { + int xrdb_pid; + int read_fd = xrdb_popen(&xrdb_pid); + if (read_fd >= 0) { + int dpi_read = xrdb_parse_for_dpi(read_fd); + int child_status; + waitpid(xrdb_pid, &child_status, 0); + close(read_fd); + return dpi_read; + } + return -1; +} + +#endif diff --git a/src/xrdb_parse.h b/src/xrdb_parse.h new file mode 100644 index 00000000..e7ef8396 --- /dev/null +++ b/src/xrdb_parse.h @@ -0,0 +1,6 @@ +#ifndef XRDB_PARSE_H +#define XRDB_PARSE_H + +extern int xrdb_find_dpi(); + +#endif