ucx update
[uwplayer.git] / application / settings.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "settings.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <pthread.h>
33
34 #include "main.h"
35 #include "utils.h"
36 #include "json.h"
37
38 #include <cx/map.h>
39 #include <cx/hash_map.h>
40 //include <ucx/properties.h>
41 #include <cx/buffer.h>
42 #include <cx/utils.h>
43
44 #define CONFIG_BASE_DIR ".config"
45 #define UWP_CONFIG_DIR  "uwplayer"
46 #define UWP_CONFIG_FILE "uwplayer.conf"
47
48 #define JS_READ_BUFSIZE 4096
49
50 static void* player_bin_search_thread(void *data);
51 static void conf_load_global_settings(void);
52
53 static char *uwp_config_dir;
54
55 /*
56  * root json config object
57  */
58 static JSONObject *uwp_config;
59
60 /*
61  * global settings from json config converted to key/value pairs
62  */
63 static CxMap *uwp_settings; 
64
65 /*
66  * default settings
67  */
68 static CxMap *uwp_default;
69
70 static int check_config_dir(void) {
71     char *home = getenv("HOME");
72     if(!home) {
73         return 1;
74     }
75     
76     char *cfg_dir = util_concat_path(home, CONFIG_BASE_DIR);
77     int ret = 0;
78     if(mkdir(cfg_dir, S_IRWXU)) {
79         if(errno != EEXIST) {
80             fprintf(stderr, "Error: Cannot access %s: %s\n", cfg_dir, strerror(errno));
81             ret = 1;
82         }
83     }
84     
85     if(!ret) {
86         uwp_config_dir = util_concat_path(cfg_dir, UWP_CONFIG_DIR);
87         if(mkdir(uwp_config_dir, S_IRWXU)) {
88             if(errno != EEXIST) {
89                 fprintf(stderr, "Error: Cannot access %s: %s\n", uwp_config_dir, strerror(errno));
90                 ret = 1;
91             }
92         }
93     }
94     
95     free(cfg_dir);
96     return ret;
97 }
98
99 int load_config(void) {
100     if(check_config_dir()) {
101         return 1;
102     }
103     
104     uwp_settings = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
105     uwp_default = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
106     
107     char *cfgfile_path = util_concat_path(uwp_config_dir, UWP_CONFIG_FILE);
108     FILE *cfgfile = fopen(cfgfile_path, "r");
109     free(cfgfile_path);
110     
111     int ret = 0;
112     if(cfgfile) {
113         JSONParser *parser = json_parser_new();
114         
115         JSONValue *value = NULL;
116         char buf[JS_READ_BUFSIZE];
117         size_t r;
118         
119         while((ret = json_read_value(parser, &value)) >= 0) {
120             if(ret == 0) {
121                 r = fread(buf, 1, JS_READ_BUFSIZE, cfgfile);
122                 if(r == 0) {
123                     break;
124                 }
125                 json_parser_fill(parser, buf, r);
126             } else {
127                 break;
128             }
129         }
130         
131         json_parser_free(parser);
132         
133         if(value) {
134             if(value->type == JSON_OBJECT) {
135                 ret = 0;
136                 uwp_config = &value->value.object;
137                 conf_load_global_settings();
138             } else {
139                 ret = 1;
140             }
141         } else {
142             ret = 1;
143         }
144         
145         
146         fclose(cfgfile);
147
148         if(ret) {
149             return ret;
150         }
151     }
152  
153     
154     // check if mpv or mplayer binaries are configured
155     char *player_bin = cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN));
156     char *player_type = cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_TYPE));
157     
158     if(!player_bin) {
159         // try to find the mpv or mplayer binary path
160         pthread_t st;
161         pthread_create(&st, NULL, player_bin_search_thread, NULL);
162     } else if(!player_type) {
163         fprintf(stderr, "Warning: unknown player type (mplayer, mpv)\n");
164     }
165     
166     return 0;
167 }
168
169 static void conf_load_global_settings(void) {
170     JSONValue *settings = json_obj_get(uwp_config, "settings");
171     if(!settings) {
172         return;
173     }
174     
175     if(settings->type != JSON_OBJECT) {
176         fprintf(stderr, "Warning: 'settings' not an object\n");
177         return;
178     }
179     
180     JSONObject *s = &settings->value.object;
181     
182     for(size_t i=0;i<s->size;i++) {
183         JSONObjValue *gs = &s->values[i];
184         if(gs->value->type == JSON_STRING) {
185             cxMapPut(uwp_settings, cx_hash_key_str(gs->name), strdup(gs->value->value.string.string));
186         }
187     }
188 }
189
190 static char* get_which_output(FILE *f, CxBuffer *buf) {
191     buf->pos = 0;
192     buf->size = 0;
193     cx_stream_copy(f, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
194     if(!pclose(f)) {
195         cxBufferPut(buf, 0);
196         size_t i;
197         for(i=0;i<buf->pos;i++) {
198             if(buf->space[i] == '\n') {
199                 buf->space[i] = 0;
200                 break;
201             }
202         }
203         return buf->space;
204     }
205     return NULL;
206 }
207
208 static Boolean finish_bin_search(XtPointer data) {
209     PlayerInfo *playerInfo = data;
210     cxMapPut(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN), playerInfo->bin);
211     cxMapPut(uwp_settings, cx_hash_key_str(UWP_PLAYER_TYPE), playerInfo->type);
212     free(playerInfo);
213     return 0;
214 }
215
216 static void* player_bin_search_thread(void *data) {
217     CxBuffer buf;
218     cxBufferInit(&buf, NULL, 256, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
219     
220     FILE *f = popen("which mpv", "r");
221     if(f) {
222         char *bin = get_which_output(f, &buf);
223         if(bin) {
224             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
225             playerInfo->bin = strdup(bin);
226             playerInfo->type = strdup("mpv");
227             AppExecProc(finish_bin_search, playerInfo);
228             
229             cxBufferDestroy(&buf);
230             return NULL;
231         }
232     }
233     
234     f = popen("which mplayer", "r");
235     if(f) {
236         char *bin = get_which_output(f, &buf);
237         if(bin) {
238             PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
239             playerInfo->bin = strdup(bin);
240             playerInfo->type = strdup("mplayer");
241             AppExecProc(finish_bin_search, playerInfo);
242         }
243     }
244     
245     cxBufferDestroy(&buf);
246     return NULL;
247 }
248
249 char* SettingsGetPlayerBin(void) {
250     return cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN));
251 }