1 /*
2  * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
3  *
4  * Distributed under terms of the GPL3 license.
5  */
6 
7 #pragma once
8 
9 #include "../state.h"
10 
11 static inline float
PyFloat_AsFloat(PyObject * o)12 PyFloat_AsFloat(PyObject *o) {
13     return (float)PyFloat_AsDouble(o);
14 }
15 
16 static inline color_type
color_as_int(PyObject * color)17 color_as_int(PyObject *color) {
18     if (!PyTuple_Check(color)) { PyErr_SetString(PyExc_TypeError, "Not a color tuple"); return 0; }
19 #define I(n, s) ((PyLong_AsUnsignedLong(PyTuple_GET_ITEM(color, n)) & 0xff) << s)
20     return (I(0, 16) | I(1, 8) | I(2, 0)) & 0xffffff;
21 #undef I
22 }
23 
24 static inline color_type
active_border_color(PyObject * color)25 active_border_color(PyObject *color) {
26     if (color == Py_None) return 0x00ff00;
27     return color_as_int(color);
28 }
29 
30 
31 static inline monotonic_t
parse_s_double_to_monotonic_t(PyObject * val)32 parse_s_double_to_monotonic_t(PyObject *val) {
33     return s_double_to_monotonic_t(PyFloat_AsDouble(val));
34 }
35 
36 static inline monotonic_t
parse_ms_long_to_monotonic_t(PyObject * val)37 parse_ms_long_to_monotonic_t(PyObject *val) {
38     return ms_to_monotonic_t(PyLong_AsUnsignedLong(val));
39 }
40 
41 static inline int
resolve_mods(int kitty_mod,int mods)42 resolve_mods(int kitty_mod, int mods) {
43     if (mods & GLFW_MOD_KITTY) {
44         mods = (mods & ~GLFW_MOD_KITTY) | kitty_mod;
45     }
46     return mods;
47 }
48 
49 static WindowTitleIn
window_title_in(PyObject * title_in)50 window_title_in(PyObject *title_in) {
51     const char *in = PyUnicode_AsUTF8(title_in);
52     switch(in[0]) {
53         case 'a': return ALL;
54         case 'w': return WINDOW;
55         case 'm': return MENUBAR;
56         case 'n': return NONE;
57         default: break;
58     }
59     return ALL;
60 }
61 
62 static BackgroundImageLayout
bglayout(PyObject * layout_name)63 bglayout(PyObject *layout_name) {
64     const char *name = PyUnicode_AsUTF8(layout_name);
65     switch(name[0]) {
66         case 't': return TILING;
67         case 'm': return MIRRORED;
68         case 's': return SCALED;
69         default: break;
70     }
71     return TILING;
72 }
73 
74 static void
background_image(PyObject * src,Options * opts)75 background_image(PyObject *src, Options *opts) {
76     if (opts->background_image) free(opts->background_image);
77     opts->background_image = NULL;
78     if (src == Py_None || !PyUnicode_Check(src)) return;
79     Py_ssize_t sz;
80     const char *s = PyUnicode_AsUTF8AndSize(src, &sz);
81     opts->background_image = calloc(sz + 1, 1);
82     if (opts->background_image) memcpy(opts->background_image, s, sz);
83 }
84 
85 
86 static MouseShape
pointer_shape(PyObject * shape_name)87 pointer_shape(PyObject *shape_name) {
88     const char *name = PyUnicode_AsUTF8(shape_name);
89     switch(name[0]) {
90         case 'a': return ARROW;
91         case 'h': return HAND;
92         case 'b': return BEAM;
93         default: break;
94     }
95     return BEAM;
96 }
97 
98 static inline void
free_url_prefixes(void)99 free_url_prefixes(void) {
100     OPT(url_prefixes).num = 0;
101     OPT(url_prefixes).max_prefix_len = 0;
102     if (OPT(url_prefixes).values) {
103         free(OPT(url_prefixes.values));
104         OPT(url_prefixes).values = NULL;
105     }
106 }
107 
108 static void
url_prefixes(PyObject * up,Options * opts)109 url_prefixes(PyObject *up, Options *opts) {
110     if (!PyTuple_Check(up)) { PyErr_SetString(PyExc_TypeError, "url_prefixes must be a tuple"); return; }
111     free_url_prefixes();
112     opts->url_prefixes.values = calloc(PyTuple_GET_SIZE(up), sizeof(UrlPrefix));
113     if (!opts->url_prefixes.values) { PyErr_NoMemory(); return; }
114     opts->url_prefixes.num = PyTuple_GET_SIZE(up);
115     for (size_t i = 0; i < opts->url_prefixes.num; i++) {
116         PyObject *t = PyTuple_GET_ITEM(up, i);
117         if (!PyUnicode_Check(t)) { PyErr_SetString(PyExc_TypeError, "url_prefixes must be strings"); return; }
118         opts->url_prefixes.values[i].len = MIN(arraysz(opts->url_prefixes.values[i].string) - 1, (size_t)PyUnicode_GET_LENGTH(t));
119         int kind = PyUnicode_KIND(t);
120         opts->url_prefixes.max_prefix_len = MAX(opts->url_prefixes.max_prefix_len, opts->url_prefixes.values[i].len);
121         for (size_t x = 0; x < opts->url_prefixes.values[i].len; x++) {
122             opts->url_prefixes.values[i].string[x] = PyUnicode_READ(kind, PyUnicode_DATA(t), x);
123         }
124     }
125 }
126 
127 static char_type*
list_of_chars(PyObject * chars)128 list_of_chars(PyObject *chars) {
129     if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; }
130     char_type *ans = calloc(PyUnicode_GET_LENGTH(chars) + 1, sizeof(char_type));
131     if (ans) {
132         for (ssize_t i = 0; i < PyUnicode_GET_LENGTH(chars); i++) {
133             ans[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i);
134         }
135     }
136     return ans;
137 }
138 
139 static void
url_excluded_characters(PyObject * chars,Options * opts)140 url_excluded_characters(PyObject *chars, Options *opts) {
141     free(opts->url_excluded_characters);
142     opts->url_excluded_characters = list_of_chars(chars);
143 }
144 
145 static void
select_by_word_characters(PyObject * chars,Options * opts)146 select_by_word_characters(PyObject *chars, Options *opts) {
147     free(opts->select_by_word_characters);
148     opts->select_by_word_characters = list_of_chars(chars);
149 }
150 
151 static void
tab_bar_style(PyObject * val,Options * opts)152 tab_bar_style(PyObject *val, Options *opts) {
153     opts->tab_bar_hidden = PyUnicode_CompareWithASCIIString(val, "hidden") == 0 ? true: false;
154 }
155 
156 static void
tab_bar_margin_height(PyObject * val,Options * opts)157 tab_bar_margin_height(PyObject *val, Options *opts) {
158     if (!PyTuple_Check(val) || PyTuple_GET_SIZE(val) != 2) {
159         PyErr_SetString(PyExc_TypeError, "tab_bar_margin_height is not a 2-item tuple");
160         return;
161     }
162     opts->tab_bar_margin_height.outer = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 0));
163     opts->tab_bar_margin_height.inner = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 1));
164 }
165 
166 #define read_adjust(name) { \
167     if (PyFloat_Check(al)) { \
168         opts->name##_frac = (float)PyFloat_AsDouble(al); \
169         opts->name##_px = 0; \
170     } else { \
171         opts->name##_frac = 0; \
172         opts->name##_px = (int)PyLong_AsLong(al); \
173     } \
174 }
175 
176 static void
adjust_line_height(PyObject * al,Options * opts)177 adjust_line_height(PyObject *al, Options *opts) { read_adjust(adjust_line_height); }
178 static void
adjust_column_width(PyObject * al,Options * opts)179 adjust_column_width(PyObject *al, Options *opts) { read_adjust(adjust_column_width); }
180 static void
adjust_baseline(PyObject * al,Options * opts)181 adjust_baseline(PyObject *al, Options *opts) { read_adjust(adjust_baseline); }
182 #undef read_adjust
183