summaryrefslogblamecommitdiffstats
path: root/xlnch.c
blob: b3299216fe91b92004aee51409978d1d4c8ce21f (plain) (tree)






















                                                                                  
                  





                     
 


                            















                                                            
                         
 
                     



                 































































































                                                                                                                         







                              
 


                                                                       
     











                                                       
     
                 



                

















                                                                                              
                    

                               
                                            










                                                                                                 
     






                                                                



         

                     
 
             

                
               




















































                                                                                             







                                

               
































                                                                          
/**
 * xlnch -- D e s c r i p t i o n
 * Copyright (C) 2019  Anastasis Grammenos
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/* Basic X code from:
 * Brian Hammond 2/9/96. http://mech.math.msu.su/~nap/2/GWindow/xintro.html
 */

#include <X11/X.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <wordexp.h>

#include <gtk/gtk.h>

#include "gtk/gtkshortcut.h"
#include "util.h"

#define BUF_SIZE 2048
#define MAX_ENTRIES 32

#define FONTNAME "-*-terminus-medium-*-*-*-16-*-*-*-*-*-*-*"

char buf[BUF_SIZE] = "", cur[BUF_SIZE] = "";

struct lnch_t {
    int exits;
    char key;
    char desc[512];
    char cmd[1024];
} lnch[MAX_ENTRIES];

GtkWidget *window = NULL;

unsigned int idx = 0;

int w_width = 0;
int w_height = 0;

int parse_line(char *line)
{
    char *token;
    char delim[2] = ":";

    /* Copy buf to cur for error reporting */
    strcpy(cur, buf);

    trim(buf);

    if (strlen(line) == 0 || line[0] == '#')
	return 2;

    lnch[idx].exits = 1;

    if (line[0] == '&') {
	lnch[idx].exits = 0;
	line++;
    }

    /* Get key entry */
    token = strtok(line, delim);

    if (strlen(token) != 1)
	return 1;

    lnch[idx].key = token[0];
    line += 2;			// pass the key, pass the `:`

    /* Get description */
    token = strtok(NULL, delim);

    if (!token)
	return 1;

    strcpy(lnch[idx].desc, token);
    line += (strlen(token) + 1);	// pass the decription, pass the `:`

    /* Get command */
    strcpy(lnch[idx].cmd, line);

    /* If description is epmty command becomes the descritpion */
    if (!strlen(lnch[idx].cmd))
	strcpy(lnch[idx].cmd, lnch[idx].desc);

    w_width = MAX((int) strlen(lnch[idx].desc), w_width);
    idx++;
    return 0;
}

int run_cmd(char *cmd)
{
    wordexp_t p;
    int rc = wordexp(cmd, &p, 0);
    switch (rc) {
    case WRDE_BADCHAR:
	printf
	    ("Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, }.\n");
	break;
    case WRDE_BADVAL:
	printf
	    ("An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error.\n");
	break;
    case WRDE_CMDSUB:
	printf
	    ("Command substitution requested, but the WRDE_NOCMD flag told us to consider this an error.\n");
	break;
    case WRDE_NOSPACE:
	printf("Out of memory.\n");
	break;
    case WRDE_SYNTAX:
	printf
	    ("Shell syntax error, such as unbalanced parentheses or unmatched quotes.\n");
	break;
    default:
	printf("Running:");
	for (size_t i = 0; i < p.we_wordc; i++) {
	    printf(" %s", p.we_wordv[i]);
	}
	printf("\n");

	/* double fork from i3wm's exec implementation */
	if (fork() == 0) {
	    setsid();
	    if (fork() == 0) {
		execvp(p.we_wordv[0], p.we_wordv);
		/* never reached */
	    }
	    _exit(EXIT_SUCCESS);
	}
	wait(0);
    }
    wordfree(&p);
    return rc;
}

gboolean
key_pressed_callback (
  GtkEventControllerKey* self,
  guint keyval,
  guint keycode,
  GdkModifierType state,
  gpointer user_data
)
{
    if ((state == GDK_CONTROL_MASK && keycode == 42) || keycode == 9) {
        gtk_window_destroy(GTK_WINDOW(window));
        die("diead");
    }
    for (unsigned int i = 0; i < idx; i++) {
        if (lnch[i].key == keyval) {
            if (lnch[i].exits) {
                gtk_window_destroy(GTK_WINDOW(window));
            }
            run_cmd(lnch[i].cmd);
            if (lnch[i].exits) {
              die("diead");
            }
            gtk_widget_grab_focus (window);

        }
    }
    return false;
}

void draw_text()
{
    g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL);
    window = gtk_window_new();
    gtk_window_set_title(GTK_WINDOW(window), "xlnch");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 440);
    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);


    GtkEventController* ec = gtk_event_controller_key_new();
    g_signal_connect(G_OBJECT(ec), "key_pressed", G_CALLBACK (key_pressed_callback), NULL);

    gtk_widget_add_controller (window, GTK_EVENT_CONTROLLER (ec));

    /* GtkWidget *spinner = gtk_spinner_new(); */
    /* gtk_window_set_child(GTK_WINDOW(window), spinner); */
    /* gtk_spinner_start(GTK_SPINNER(spinner)); */


    char text[2048];
    char bigbuf[1024*100] = "";
    //int x = 10, y = 2;
    for (unsigned int i = 0; i < idx; i++) {
        if (lnch[i].exits) {
            sprintf(text, "%c: %s\n", lnch[i].key, lnch[i].desc);
            strcat(bigbuf, text);
    	  // XSetForeground(display, gc, WhitePixel(display, screen));
    	} else {
            sprintf(text, "<span foreground=\"red\">%c</span>: %s\n", lnch[i].key, lnch[i].desc);
            strcat(bigbuf, text);
    	  // XSetForeground(display, gc, red.pixel);
    	}

	// XDrawString(display, win, gc, x, y += 13, text, strlen(text));
    }
    GtkWidget *label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label),bigbuf);
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
    gtk_window_set_child(GTK_WINDOW(window), label);

    gtk_window_present(GTK_WINDOW(window));

}

int run()
{
    //char text[255];
    //int flag, rc;

    //init();
    draw_text();

    while (1) {
        g_main_context_iteration(NULL, TRUE);
	// XNextEvent(display, &event);

	// if (event.type == Expose && event.xexpose.count == 0) {
	//     /* the window was exposed redraw it! */
	//     XClearWindow(display, win);
	//     draw_text();
	// }

	// /* Keep window in view */
	// else if (event.type == VisibilityNotify &&
	// 	 event.xvisibility.state != VisibilityUnobscured)
	//     XRaiseWindow(display, win);

	// else if (event.type == KeyPress) {
	//     /* C-g and ESC quit */
	//     if ((event.xkey.state == 4 && event.xkey.keycode == 42)	/* => C-g  */
	// 	||event.xkey.keycode == 9) {	/* => ESC  */
	// 	close_x();
	// 	return 0;
	//     }

	//     for (unsigned int i = 0; i < idx; i++) {
 //              char tmp[128] = "";
 //              // get configured key
 //              tmp[0] = lnch[i].key;
 //              tmp[1] = '\0';
 //              // convert to keysym, and then to keycode Keycodes are consistent
 //              // across layouts but may vary between different keyboard models.
 //              // For example, the keycode for the "A" key might differ between
 //              // a US QWERTY keyboard and a French AZERTY keyboard.
 //              if (XKeysymToKeycode(display, XStringToKeysym(tmp)) == event.xkey.keycode) {
	// 	    rc = run_cmd(lnch[i].cmd);
	// 	    flag = 1;
	// 	    if (lnch[i].exits) {
	// 		close_x();
	// 		return rc;
	// 	    }
	// 	}
	//     }

	//     /* Q and q quit as well, as long as they don't match anything */
	//     if (!flag && event.xkey.keycode == 24) {	/* Q or q */
	// 	close_x();
	// 	return 0;
	//     }
	// }

	// else if (event.type == FocusIn) {
	//     if (event.xfocus.window != win) {
	// 	grabfocus();
	//     }
	// }
    }
}

int main(int argc, char *argv[])
{
    int rc, n = 1;
    FILE *f;

    gtk_init();

    if ((argc > 1) &&
	(!strcmp(argv[1], "--help") ||
	 !strcmp(argv[1], "-h") || !strcmp(argv[1], "-?"))) {
	printf("Usage:\n%s [xlnchrc]\n", argv[0]);
	printf("\nxlnchrc format:\n[&]<key>:[<description>]:<command>\n");
	return 0;
    }

    if (argc == 1 || !strcmp(argv[1], "-"))
	f = stdin;
    else {
	f = fopen(argv[1], "r");
	if (!f)
	    die("Cannot open file %s", argv[1]);
    }

    while (fgets(buf, BUF_SIZE, f)) {
	rc = parse_line(buf);
	if (rc == 1) {
	    fprintf(stderr, "%s:%d: cannot parse ā€˜%sā€™\n",
		    argc == 1
		    || !strcmp(argv[1], "-") ? "stdin" : argv[1], n, cur);
	} else if (rc == 0) {
	    w_height++;
	}
	n++;
    }

    if (f != stdin)
	fclose(f);

    return run();
}