/**
* 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();
}