add single instance mode
[uwplayer.git] / application / main.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 <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <locale.h>
27 #include <time.h>
28 #include <inttypes.h>
29 #include <sys/stat.h>
30
31 #include "window.h"
32 #include "main.h"
33 #include "settings.h"
34
35 #include <cx/buffer.h>
36 #include <cx/utils.h>
37
38 static XtAppContext app;
39 static Display *display;
40 static Widget toplevel_window;
41
42 static char *open_file_arg;
43
44 static int event_pipe[2];
45
46 static String fallback[] = {
47         "*renderTable: rt",
48         "*rt*fontType: FONT_IS_XFT",
49         "*rt*fontName: Sans",
50         "*rt*fontSize: 9",
51         
52         "*pbbutton.shadowThickness: 1",
53         "*pbbutton.highlightThickness: 1",
54         
55         "*XmText.baseTranslations: #override\\n" \
56                                 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \
57                                 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \
58                                 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \
59                                 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n",
60         "*XmTextField.baseTranslations: #override\\n" \
61                                 "Ctrl~Alt~Meta<KeyPress>v: paste-clipboard()\\n" \
62                                 "Ctrl~Alt~Meta<KeyPress>c: copy-clipboard()\\n" \
63                                 "Ctrl~Alt~Meta<KeyPress>x: cut-clipboard()\\n" \
64                                 "Ctrl~Alt~Meta<KeyPress>u: delete-to-start-of-line()\\n",
65         NULL
66 };
67
68 static String langProc(Display *dp, String xnl, XtPointer closure) {
69     setlocale(LC_ALL, xnl);
70     setlocale(LC_NUMERIC, "C");
71     return setlocale(LC_ALL, NULL);
72 }
73
74 typedef struct EventLoopCB {
75     XtWorkProc proc;
76     XtPointer data;
77 } EventLoopCB;
78
79 static void input_proc(XtPointer data, int *source, XtInputId *iid) {
80     EventLoopCB cb[16];
81     ssize_t r = read(event_pipe[0], cb, sizeof(EventLoopCB)*16);
82     size_t n = r / sizeof(EventLoopCB);
83     for(int i=0;i<n;i++) {
84         cb[i].proc(cb[i].data);
85     }
86 }
87
88 int main(int argc, char** argv) {  
89     // disable stdout buffering, because the netbeans's internal terminal
90     // has a bug on freebsd and doesn't flush the output after a newline
91     setvbuf(stdout, NULL, _IONBF, 0);
92     
93     // init event pipe for xt event loop
94     if(pipe(event_pipe)) {
95         perror("pipe");
96         return 2;
97     }
98      
99     // initialize toolkit
100     XtToolkitInitialize();
101     XtSetLanguageProc(NULL, langProc, NULL);
102     app = XtCreateApplicationContext();
103     XtAppSetFallbackResources(app, fallback);
104        
105     display =  XtOpenDisplay(app, NULL, APP_NAME, APP_CLASS, NULL, 0, &argc, argv);
106     
107     if(argc > 1) {
108         struct stat s;
109         if(stat(argv[1], &s)) {
110             fprintf(stderr, "Cannot open file: %s\n", argv[1]);
111             perror("");
112             return 1;
113         }
114         open_file_arg = argv[1];
115     }
116     
117     // load settings
118     if(load_config()) {
119         return 1;
120     }
121     
122     // try single instance open
123     if(open_file_arg) {
124         char *instance_path = InstanceFilePath(display);
125         int instance_fd = ConnectToInstance(instance_path);
126         free(instance_path);
127         
128         if(instance_fd >= 0) {
129             write(instance_fd, "open ", 5);
130             write(instance_fd, open_file_arg, strlen(open_file_arg));
131             write(instance_fd, "\n", 1);
132             close(instance_fd);
133             return 0;
134         }
135     }
136     
137     XtAppAddInput(
138             app,
139             event_pipe[0],
140             (XtPointer)XtInputReadMask,
141             input_proc,
142             NULL);
143     
144     MainWindow *window = WindowCreate(display);
145     toplevel_window = window->window;
146     
147     // random numbers used for creating tmp dirs and for random playback
148     srand(time(NULL));
149     
150     WindowShow(window);
151     AppMainLoop(app);
152     
153     return 0;
154 }
155
156 XtAppContext* GetAppContext(void) {
157     return &app;
158 }
159
160 void ApplicationExit(void) {
161     XtAppSetExitFlag(app);
162 }
163
164 void AppExecProc(XtWorkProc proc, XtPointer data) {
165     EventLoopCB cb;
166     cb.proc = proc;
167     cb.data = data;
168     write(event_pipe[1], &cb, sizeof(cb));
169 }
170
171 char* GetOpenFileArg(void) {
172     return open_file_arg;
173 }
174
175 void CleanOpenFileArg(void) {
176     open_file_arg = NULL;
177 }
178
179 static Window app_player_window = 0;
180
181 void SetPlayerWindow(Window w) {
182     app_player_window = w;
183 }
184
185 /*
186  * Extended Xt main loop, that also handles external window events
187  */
188 void AppMainLoop(XtAppContext app) {
189     while(!XtAppGetExitFlag(app)) {
190         XEvent event;
191         XtAppNextEvent(app, &event);
192         
193         if(app_player_window != 0 && event.xany.window == app_player_window) {
194             WindowHandlePlayerEvent(GetMainWindow(), &event);
195         } else {
196             XtDispatchEvent(&event);
197         }
198     }
199 }