2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.mozilla.org/MPL/
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9 * License for the specific language governing rights and limitations
12 * The Original Code is NanoLauncher.
14 * The Initial Developer of the Original Code is Alex Holden.
15 * Portions created by Alex Holden are Copyright (C) 2000
16 * Alex Holden <alex@linuxhacker.org>. All Rights Reserved.
20 * Alternatively, the contents of this file may be used under the terms
21 * of the GNU General Public license (the "[GNU] License"), in which case the
22 * provisions of [GNU] License are applicable instead of those
23 * above. If you wish to allow use of your version of this file only
24 * under the terms of the [GNU] License and not to allow others to use
25 * your version of this file under the MPL, indicate your decision by
26 * deleting the provisions above and replace them with the notice and
27 * other provisions required by the [GNU] License. If you do not delete
28 * the provisions above, a recipient may use your version of this file
29 * under either the MPL or the [GNU] License.
33 * A simple application launcher for Nano-X by Alex Holden.
35 * The application needs to be started with the first argument specifying
36 * the location of it's configuration file. The format of the file is
37 * extremely simple- each line can contain either a comment (indicated by
38 * beginning the line with a '#' symbol) or an item description.
39 * An item description consists of the name of the item (the title which
40 * appears underneath the icon on the launcher button) followed by the name
41 * of the icon file (or '-' for no icon) and the command to execute when the
42 * item is clicked on. The command can optionally be followed by a limited
43 * number of arguments to pass to the program when it is executed (increase
44 * MAX_ARGUMENTS in launcher.h if you need more). The program will currently
45 * only allow one icon size (specified at compile time by the ICON_WIDTH and
46 * ICON_HEIGHT parameters). The program only loads each icon file once even if
47 * it is used multiple times, so you can save a small amount of memory by
48 * using the same icon for several programs. If you want to change the size
49 * of the item buttons, change ITEM_WIDTH and ITEM_HEIGHT in launcher.h.
50 * The way the launcher decides whether to draw a vertical panel on the left
51 * hand side of the screen or a horizontal panel along the bottom is by
52 * looking at the width and height of the screen- the panel will be placed on
53 * the side on portrait screens and on the bottom on landscape screens.
65 #define MWINCLUDECOLORS
69 void reaper(int signum) { while(waitpid(WAIT_ANY, NULL, WNOHANG) > 0); }
71 void *my_malloc(size_t size)
75 if(!(ret = malloc(size))) {
76 fprintf(stderr, "Out of memory\n");
85 fprintf(stderr, "Usage: launcher <config-file>\n");
89 prog_item *make_prog_item(char *buf, int lineno)
91 char *p, *pp, *command;
97 prog = my_malloc(sizeof(prog_item));
99 for(n = 0; n < MAX_ARGUMENTS; n++) prog->argv[n] = NULL;
101 while(isspace(*p)) p++;
103 fprintf(stderr, "Premature end of line on line %d of config "
108 while(*p && (!isspace(*p))) p++;
110 if(!(prog->command = strdup(command))) {
115 while(--pp != command) {
121 if(!(prog->argv[0] = strdup(pp))) {
129 while(isspace(*p)) p++;
132 while(*p && (!isspace(*p))) p++;
134 if(!(prog->argv[n] = strdup(pp))) {
135 for(n = MAX_ARGUMENTS; n; n--)
136 if(prog->argv[n]) free(prog->argv[n]);
141 if(++n == (MAX_ARGUMENTS - 1)) {
142 fprintf(stderr, "Too many arguments on line "
143 "%d of the config file\n", lineno);
151 fprintf(stderr, "Out of memory parsing line %d of the config "
156 void set_window_background_colour(char *buf, int lineno)
158 GR_WM_PROPERTIES props;
161 while(isspace(*p)) p++;
163 fprintf(stderr, "Premature end of line on line %d of config "
168 while(*p && (!isspace(*p))) p++;
171 if(!strcmp(pp, "BLACK")) props.background = BLACK;
172 else if(!strcmp(pp, "BLUE")) props.background = BLUE;
173 else if(!strcmp(pp, "GREEN")) props.background = GREEN;
174 else if(!strcmp(pp, "CYAN")) props.background = CYAN;
175 else if(!strcmp(pp, "RED")) props.background = RED;
176 else if(!strcmp(pp, "MAGENTA")) props.background = MAGENTA;
177 else if(!strcmp(pp, "BROWN")) props.background = BROWN;
178 else if(!strcmp(pp, "LTGRAY")) props.background = LTGRAY;
179 else if(!strcmp(pp, "GRAY")) props.background = GRAY;
180 else if(!strcmp(pp, "LTBLUE")) props.background = LTBLUE;
181 else if(!strcmp(pp, "LTGREEN")) props.background = LTGREEN;
182 else if(!strcmp(pp, "LTCYAN")) props.background = LTCYAN;
183 else if(!strcmp(pp, "LTRED")) props.background = LTRED;
184 else if(!strcmp(pp, "LTMAGENTA")) props.background = LTMAGENTA;
185 else if(!strcmp(pp, "YELLOW")) props.background = YELLOW;
186 else if(!strcmp(pp, "WHITE")) props.background = WHITE;
188 fprintf(stderr, "Invalid colour \"%s\" on line %d of config "
189 "file\n", pp, lineno);
193 props.flags = GR_WM_FLAGS_BACKGROUND;
194 GrSetWMProperties(GR_ROOT_WINDOW_ID, &props);
197 void parse_config_line(lstate *state, char *buf, int lineno)
199 char *p, *pp, *name, *icon;
201 litem *new_litem, *li;
203 GR_IMAGE_INFO imageinfo;
207 if((!*p) || (*p == '#') || (*p == '\n')) return;
209 while(isspace(*p)) p++;
211 while(*p && (!isspace(*p))) p++;
212 if(!*p) goto premature;
215 if(!strcmp(name, "$screensaver")) {
216 new_sitem = my_malloc(sizeof(sitem));
217 if(!(new_sitem->prog = make_prog_item(p, lineno))) {
221 new_sitem->next = NULL;
223 state->sitems = new_sitem;
224 state->cursitem = new_sitem;
226 new_sitem->next = state->sitems;
227 state->sitems = new_sitem;
230 } else if(!strcmp(name, "$screensaver_timeout")) {
231 n = strtol(p, NULL, 10);
232 GrSetScreenSaverTimeout(n);
234 } else if(!strcmp(name, "$window_background_image")) {
235 while(isspace(*p)) p++;
236 if(!*p) goto premature;
238 while(*p && (!isspace(*p))) p++;
240 state->window_background_image = strdup(pp);
242 } else if(!strcmp(name, "$window_background_mode")) {
243 state->window_background_mode = (int) strtol(p, NULL, 10);
245 } else if(!strcmp(name, "$window_background_colour")) {
246 set_window_background_colour(p, lineno);
250 while(isspace(*p)) p++;
251 if(!*p) goto premature;
253 while(*p && (!isspace(*p))) p++;
254 if(!*p) goto premature;
257 new_litem = my_malloc(sizeof(litem));
258 if(!(new_litem->name = strdup(name))) {
262 if(!(new_litem->icon = strdup(icon))) {
263 free(new_litem->name);
267 if(!(new_litem->prog = make_prog_item(p, lineno))) {
268 free(new_litem->name);
269 free(new_litem->icon);
273 new_litem->iconid = 0;
274 if(strcmp("-", icon)) {
277 if(!(strcmp(icon, li->name))) {
278 new_litem->iconid = li->iconid;
283 if(!new_litem->iconid) {
284 if(!(new_litem->iconid = GrLoadImageFromFile(icon, 0))){
285 fprintf(stderr, "Couldn't load icon \"%s\"\n",
288 GrGetImageInfo(new_litem->iconid, &imageinfo);
289 if((imageinfo.width != ICON_WIDTH) ||
290 (imageinfo.height != ICON_HEIGHT)) {
291 fprintf(stderr, "Icon \"%s\" is the "
292 "wrong size (%dx%d instead of %dx%d)"
293 "\n", icon, imageinfo.width,
294 imageinfo.height, ICON_WIDTH,
296 GrFreeImage(new_litem->iconid);
297 new_litem->iconid = 0;
303 new_litem->prev = NULL;
304 new_litem->next = NULL;
306 state->lastlitem = new_litem;
307 state->litems = new_litem;
309 new_litem->next = state->litems;
310 state->litems->prev = new_litem;
311 state->litems = new_litem;
319 fprintf(stderr, "Out of memory\n");
323 fprintf(stderr, "Premature end of line on line %d of config file\n",
327 void read_config(lstate *state)
331 char *buf = my_malloc(256);
333 if(!(fp = fopen(state->config_file, "r"))) {
334 fprintf(stderr, "Couldn't open config file \"%s\"\n",
339 state->litems = NULL;
340 state->numlitems = 0;
341 state->sitems = NULL;
343 while(fgets(buf, 256, fp)) {
344 parse_config_line(state, buf, lineno);
351 if(!state->numlitems) {
352 fprintf(stderr, "No valid launcher items in config file\n");
357 void draw_item(lstate *state, litem *item)
359 GR_SIZE width, height, base, x, len;
361 GrDrawImageToFit(item->wid, state->gc, ICON_X_POSITION, ICON_Y_POSITION,
362 ICON_WIDTH, ICON_HEIGHT, item->iconid);
364 len = strlen(item->name);
365 GrGetGCTextSize(state->gc, item->name, len, 0, &width, &height, &base);
366 if(width >= ITEM_WIDTH) x = 0;
367 else x = (ITEM_WIDTH - width) / 2;
369 GrText(item->wid, state->gc, x, TEXT_Y_POSITION, item->name, len, 0);
372 void handle_exposure_event(lstate *state)
374 GR_EVENT_EXPOSURE *event = &state->event.exposure;
375 litem *i = state->litems;
377 if(event->wid == state->main_window) return;
380 if(event->wid == i->wid) {
387 fprintf(stderr, "Got exposure event for unknown window %d\n",
391 void launch_program(prog_item *prog)
395 if((pid = fork()) == -1) perror("Couldn't fork");
397 if(execvp(prog->command, prog->argv) == -1)
398 fprintf(stderr, "Couldn't start \"%s\": %s\n",
399 prog->command, strerror(errno));
404 void handle_mouse_event(lstate *state)
406 GR_EVENT_MOUSE *event = &state->event.mouse;
407 litem *i = state->litems;
409 if(event->wid == state->main_window) return;
412 if(event->wid == i->wid) {
413 launch_program(i->prog);
419 fprintf(stderr, "Got mouse event for unknown window %d\n", event->wid);
422 void handle_screensaver_event(lstate *state)
424 GR_EVENT_SCREENSAVER *event = &state->event.screensaver;
426 if(event->activate != GR_TRUE) return;
429 fprintf(stderr, "Got screensaver activate event with no "
430 "screensavers defined\n");
434 state->cursitem = state->cursitem->next;
435 if(!state->cursitem) state->cursitem = state->sitems;
437 launch_program(state->cursitem->prog);
440 void handle_event(lstate *state)
442 switch(state->event.type) {
443 case GR_EVENT_TYPE_EXPOSURE:
444 handle_exposure_event(state);
446 case GR_EVENT_TYPE_BUTTON_DOWN:
447 handle_mouse_event(state);
449 case GR_EVENT_TYPE_CLOSE_REQ:
451 case GR_EVENT_TYPE_SCREENSAVER:
452 handle_screensaver_event(state);
454 case GR_EVENT_TYPE_NONE:
457 fprintf(stderr, "Got unknown event type %d\n",
463 void do_event_loop(lstate *state)
466 GrGetNextEvent(&state->event);
468 } while(state->event.type != GR_EVENT_TYPE_CLOSE_REQ);
471 void initialise(lstate *state)
474 GR_IMAGE_ID back_image;
475 GR_IMAGE_INFO imageinfo;
476 int rows = 1, columns = 1, width, height, x = 0, y = 1;
477 GR_WM_PROPERTIES props;
481 fprintf(stderr, "Couldn't connect to Nano-X server\n");
485 state->window_background_mode = 0;
486 state->window_background_image = NULL;
490 GrGetScreenInfo(&si);
492 if(si.rows > si.cols) {
493 rows = state->numlitems;
494 while((((rows / columns) + rows % columns) * ITEM_HEIGHT) >
498 if((columns * ITEM_WIDTH) > si.cols) goto toomany;
499 rows = (rows / columns) + (rows % columns);
500 width = columns * ITEM_WIDTH + 1 + columns;
501 height = rows * ITEM_HEIGHT + 1 + rows;
503 columns = state->numlitems;
504 while((((columns / rows) + (columns % rows)) * ITEM_WIDTH) >
508 if((rows * ITEM_HEIGHT) > si.rows) goto toomany;
509 columns = (columns / rows) + (columns % rows);
510 width = columns * ITEM_WIDTH + 1 + columns;
511 height = (rows * ITEM_HEIGHT) + 1 + rows;
512 y = si.rows - (rows * ITEM_HEIGHT) - 1 - rows;
515 state->gc = GrNewGC();
516 GrSetGCForeground(state->gc, ITEM_TEXT_COLOUR);
517 GrSetGCBackground(state->gc, ITEM_BACKGROUND_COLOUR);
519 if(state->window_background_image) {
520 if(!(back_image = GrLoadImageFromFile(
521 state->window_background_image, 0))) {
522 fprintf(stderr, "Couldn't load background image\n");
524 GrGetImageInfo(back_image, &imageinfo);
525 if(!(state->background_pixmap = GrNewPixmap(
527 imageinfo.height, NULL))) {
528 fprintf(stderr, "Couldn't allocate pixmap "
529 "for background image\n");
531 GrDrawImageToFit(state->background_pixmap,
532 state->gc, 0, 0, imageinfo.width,
533 imageinfo.height, back_image);
534 GrFreeImage(back_image);
535 GrSetBackgroundPixmap(GR_ROOT_WINDOW_ID,
536 state->background_pixmap,
537 state->window_background_mode);
538 GrClearWindow(GR_ROOT_WINDOW_ID, GR_TRUE);
544 GrSelectEvents(GR_ROOT_WINDOW_ID, GR_EVENT_MASK_SCREENSAVER);
546 state->main_window = GrNewWindow(GR_ROOT_WINDOW_ID, 0, y, width, height,
547 0, ITEM_BACKGROUND_COLOUR, 0);
548 GrSelectEvents(state->main_window, GR_EVENT_MASK_CLOSE_REQ);
549 props.flags = GR_WM_FLAGS_PROPS;
550 props.props = GR_WM_PROPS_NOMOVE | GR_WM_PROPS_NODECORATE |
551 GR_WM_PROPS_NOAUTOMOVE | GR_WM_PROPS_NOAUTORESIZE;
552 GrSetWMProperties(state->main_window, &props);
554 i = state->lastlitem;
557 i->wid = GrNewWindow(state->main_window,
558 (x * ITEM_WIDTH) + x + 1,
559 (y * ITEM_HEIGHT) + y + 1, ITEM_WIDTH,
560 ITEM_HEIGHT, 1, ITEM_BACKGROUND_COLOUR,
562 GrSelectEvents(i->wid, GR_EVENT_MASK_EXPOSURE |
563 GR_EVENT_MASK_BUTTON_DOWN);
572 GrMapWindow(state->main_window);
574 signal(SIGCHLD, &reaper);
579 fprintf(stderr, "Too many items to fit on screen\n");
583 int main(int argc, char *argv[])
587 if(argc != 2) usage();
589 state = my_malloc(sizeof(lstate));
590 state->config_file = strdup(argv[1]);
594 do_event_loop(state);