1 /*
2 * LavaLauncher - A simple launcher panel for Wayland
3 *
4 * Copyright (C) 2020 - 2021 Leon Henrik Plickat
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #define _POSIX_C_SOURCE 200809L
21
22 #include<stdio.h>
23 #include<stdlib.h>
24 #include<unistd.h>
25 #include<stdbool.h>
26 #include<string.h>
27 #include<getopt.h>
28
29 #include"bar.h"
30 #include"config.h"
31 #include"event-loop.h"
32 #include"lavalauncher.h"
33 #include"str.h"
34 #include"wayland-connection.h"
35 #include"misc-event-sources.h"
36
37 /* The context is used basically everywhere. So instead of passing pointers
38 * around, just have it global.
39 */
40 struct Lava_context context = {0};
41
handle_command_flags(int argc,char * argv[])42 static bool handle_command_flags (int argc, char *argv[])
43 {
44 const char usage[] =
45 "Usage: lavalauncher [options...]\n"
46 " -c <path>, --config <path> Path to config file.\n"
47 " -h, --help Print this help text.\n"
48 " -v, --verbose Enable verbose output.\n"
49 " -V, --version Show version.\n"
50 "\n"
51 "The configuration syntax is documented in the man page.\n";
52
53 static struct option opts[] = {
54 {"config", required_argument, NULL, 'c'},
55 {"help", no_argument, NULL, 'h'},
56 {"verbose", no_argument, NULL, 'v'},
57 {"version", no_argument, NULL, 'V'},
58 {0, 0, 0, 0 }
59 };
60
61 extern int optind;
62 optind = 0;
63 extern char *optarg;
64 for (int c; (c = getopt_long(argc, argv, "c:hvV", opts, &optind)) != -1 ;) switch (c)
65 {
66 case 'c':
67 set_string(&context.config_path, optarg);
68 break;
69
70 case 'h':
71 fputs(usage, stderr);
72 context.ret = EXIT_SUCCESS;
73 return false;
74
75 case 'v':
76 context.verbosity++;
77 break;
78
79 case 'V':
80 fputs("LavaLauncher version " LAVALAUNCHER_VERSION"\n", stderr);
81 context.ret = EXIT_SUCCESS;
82 return false;
83
84 default:
85 return false;
86 }
87
88 return true;
89 }
90
get_default_config_path(void)91 static bool get_default_config_path (void)
92 {
93 struct
94 {
95 const char *fmt;
96 const char *env;
97 } paths[] = {
98 { .fmt = "./lavalauncher.conf", .env = NULL },
99 { .fmt = "%s/lavalauncher/lavalauncher.conf", .env = getenv("XDG_CONFIG_HOME") },
100 { .fmt = "%s/.config/lavalauncher/lavalauncher.conf", .env = getenv("HOME") },
101 { .fmt = "/usr/local/etc/lavalauncher/lavalauncher.conf", .env = NULL },
102 { .fmt = "/etc/lavalauncher/lavalauncher.conf", .env = NULL }
103 };
104
105 FOR_ARRAY(paths, i)
106 {
107 context.config_path = get_formatted_buffer(paths[i].fmt, paths[i].env);
108 if (! access(context.config_path, F_OK | R_OK))
109 {
110 log_message(1, "[main] Using default configuration file path: %s\n", context.config_path);
111 return true;
112 }
113 free_if_set(context.config_path);
114 }
115
116 log_message(0, "ERROR: Can not find configuration file.\n"
117 "INFO: You can provide a path manually with '-c'.\n");
118 return false;
119 }
120
init_context(void)121 static void init_context (void)
122 {
123 context.ret = EXIT_FAILURE;
124 context.loop = true;
125 context.reload = false;
126 context.verbosity = 0;
127 context.config_path = NULL;
128
129 #if WATCH_CONFIG
130 context.watch = false;
131 #endif
132
133 context.display = NULL;
134 context.registry = NULL;
135
136 context.compositor = NULL;
137 context.subcompositor = NULL;
138 context.shm = NULL;
139 context.layer_shell = NULL;
140 context.xdg_output_manager = NULL;
141
142 context.river_status_manager = NULL;
143 context.need_river_status = false;
144
145 context.need_keyboard = false;
146 context.need_pointer = false;
147 context.need_touch = false;
148
149 wl_list_init(&context.bars);
150 context.last_bar = NULL;
151
152 wl_list_init(&context.outputs);
153 wl_list_init(&context.seats);
154 }
155
main(int argc,char * argv[])156 int main (int argc, char *argv[])
157 {
158 reload:
159 init_context();
160
161 if (! handle_command_flags(argc, argv))
162 return context.ret;
163
164 log_message(1, "[main] LavaLauncher: version=%s\n", LAVALAUNCHER_VERSION);
165
166 /* If the user did not provide the path to a configuration file, try
167 * the default location.
168 */
169 if ( context.config_path == NULL )
170 if (! get_default_config_path())
171 return EXIT_FAILURE;
172
173 /* Try to parse the configuration file. If this fails, there might
174 * already be heap objects, so some cleanup is needed.
175 */
176 if (! parse_config_file())
177 goto exit;
178
179 context.ret = EXIT_SUCCESS;
180
181 /* Set up the event loop and attach all event sources. */
182 struct Lava_event_loop loop;
183 event_loop_init(&loop);
184 event_loop_add_event_source(&loop, &wayland_source);
185 #if WATCH_CONFIG
186 if (context.watch)
187 event_loop_add_event_source(&loop, &inotify_source);
188 #endif
189 #if HANDLE_SIGNALS
190 event_loop_add_event_source(&loop, &signal_source);
191 #endif
192
193 /* Run the event loop. */
194 if (! event_loop_run(&loop))
195 context.ret = EXIT_FAILURE;
196
197 exit:
198 free(context.config_path);
199
200 /* Clean up objects created when parsing the configuration file. */
201 destroy_all_bars();
202
203 if (context.reload)
204 goto reload;
205 return context.ret;
206 }
207
208