Created
March 30, 2013 15:21
-
-
Save antoineMoPa/5277081 to your computer and use it in GitHub Desktop.
Small app menu for X that launches user defined commands. I created it when I used 9wm. I wanted a lightweight application launcher. This code can be useful to people who wonder how to create X applications without any widget library such as GTK or QT.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// main.c | |
// | |
// Copyright 2012 Antoine Morin-Paulhus <[email protected]> | |
// | |
// 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. | |
// Goal: allow user to create buttons that | |
// launch specific commands defined in ~/.XLauncher | |
//To build with gcc: gcc -Wall `pkg-config --libs --cflags x11` | |
/*Example ~/.XLauncher file: | |
#This is a commentary | |
# "<Label>" "<Command>" | |
# Label: max lenght: 32 characters | |
# Command: max lenght: 256 characters | |
# you must use external scripts to launch longer commands. | |
"potato""xterm -sb -geometry 300x300-1-1" | |
"USKB" "setxkbmap us" | |
"CAKB" "setxkbmap ca" | |
#use backslash ("\") when next character could | |
#be interpreted by the program in a unwanted way. | |
# ('\','"','#') | |
#Example: | |
#"Something""somecommand \"/etc/some\\ file\"" | |
*/ | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <X11/Xlib.h> | |
#include <X11/keysym.h> | |
const int BORDER = 1; | |
const int BORDERCOLOR = 0x111111; | |
const int BUTTONCOLOR = 0xdddddd; | |
const int HIGHLIGHTEDCOLOR = 0xaaaaff; | |
const int TEXTCOLOR = 0x000000; | |
typedef struct _button | |
{ | |
char label[32]; | |
char command[256]; | |
struct _button *next; | |
}button; | |
button * readButtons(); | |
void drawButtons(Display * display,Window win,GC gc,button * firstButton,int highlighted); | |
void freeButtons(button * first); | |
void execCommand(char * command); | |
int main(int ac,char **av) | |
{ | |
GC gc; | |
Display *display; | |
int screen=0; | |
Window win, root; | |
unsigned long whitePixel, blackPixel; | |
char commandToExecute[512]; | |
int i=0; | |
int clickedButton; | |
int numberOfItems=0; | |
int xPosition=0, yPosition=0; | |
if (ac == 3) | |
{ | |
if(strcmp(av[1],"-geometry")==0) | |
{ | |
XParseGeometry(av[2], &xPosition, &yPosition, NULL, NULL); | |
} | |
} | |
//We put all the buttons inside a chained list with this function | |
button * firstButton = readButtons(); | |
button * currentButton = firstButton; | |
/*Button listing + numberOfItems count*/ | |
puts("Button listing:"); | |
do | |
{ | |
printf("%s , %s\n",currentButton->label,currentButton->command); | |
currentButton = currentButton->next; | |
numberOfItems++; | |
}while(currentButton != NULL); | |
if ((display = XOpenDisplay (NULL)) == NULL) | |
{ | |
fprintf (stderr, "Can't open Display\n"); | |
exit (1); | |
} | |
gc = DefaultGC (display, screen); | |
screen = DefaultScreen (display); | |
root = RootWindow (display, screen); | |
whitePixel = WhitePixel (display, screen); | |
blackPixel = BlackPixel (display, screen); | |
win = XCreateSimpleWindow (display, root, 10, 10, 100, numberOfItems*32, | |
2, whitePixel, blackPixel); | |
XSelectInput (display, win, ExposureMask | ButtonPressMask); | |
XStoreName (display, win, "XLauncher"); | |
XMapWindow (display, win); | |
for (;;) | |
{ | |
XEvent ev; | |
XEvent previous; //Previous CLICK (not previous event) | |
XNextEvent(display,&ev); | |
switch (ev.type) | |
{ | |
case FocusIn : | |
case VisibilityNotify: | |
case MapNotify: | |
case MotionNotify: | |
case Expose : | |
drawButtons(display,win,gc,firstButton,-1); | |
XMoveWindow(display,win,xPosition,yPosition); | |
case ButtonPress: | |
if ( | |
ev.xbutton.button == 1 && | |
ev.xbutton.time-previous.xbutton.time < 700 && | |
ev.xbutton.y <= 32 * numberOfItems && | |
ev.xbutton.x <= 100 && | |
ev.xbutton.x >= 0 && | |
ev.xbutton.y >= 0 && | |
ev.xbutton.y/32 == previous.xbutton.y/32 | |
) | |
{ | |
//lets find which button was clicked on | |
clickedButton = ev.xbutton.y/32; | |
currentButton = firstButton; | |
drawButtons(display,win,gc,firstButton,clickedButton); | |
for(i=0;i<clickedButton;i++) | |
{ | |
currentButton = currentButton->next; | |
} | |
/*In order for all applications to run happily, we get to | |
the home folder before executing*/ | |
sprintf(commandToExecute,"cd %s; %s",getenv("HOME"),currentButton->command); | |
execCommand(commandToExecute); | |
//lets pause for a while | |
sleep(1); | |
drawButtons(display,win,gc,firstButton,-1); | |
} | |
else | |
{ | |
previous = ev; | |
} | |
break; | |
default : | |
break; | |
} | |
} | |
freeButtons(firstButton); | |
XFreeGC(display,gc); | |
XFree(display); | |
} | |
void skipLine(FILE * file) | |
{ | |
char buffer; | |
do | |
{ | |
buffer = fgetc(file); | |
}while(buffer != EOF && buffer != '\n'); | |
} | |
/*Reads a Label or a command and ignores '\*' stops reading after '"'*/ | |
void readString(FILE *file,char *str,int maxLenght) | |
{ | |
char buffer; | |
int i; | |
for(i=0;i<maxLenght;i++) | |
{ | |
buffer=fgetc(file); | |
if(buffer == '\\') | |
{ | |
str[i] = fgetc(file); | |
i++; | |
} | |
else if(buffer == '"') | |
{ | |
str[i] = '\0'; | |
break; | |
} | |
else | |
str[i] = buffer; | |
} | |
} | |
button * readButtons() | |
{ | |
FILE *ButtonFile; | |
char buffer; | |
char path[512]; | |
button * firstButton=NULL; | |
button * currentButton; | |
sprintf(path,"%s%s",getenv("HOME"),"/.XLauncher"); | |
ButtonFile = fopen(path,"r"); | |
if(ButtonFile == NULL) | |
{ | |
perror("An error occured while loading .XLauncher: "); | |
exit(EXIT_FAILURE); | |
} | |
for(;;) | |
{ | |
buffer = fgetc(ButtonFile); | |
if (buffer == '"') | |
{ | |
if(firstButton != NULL) | |
{ | |
currentButton->next = malloc(sizeof(button)); | |
currentButton = currentButton->next; | |
} | |
else | |
{ | |
currentButton = malloc(sizeof(button)); | |
firstButton = currentButton; | |
} | |
readString(ButtonFile,currentButton->label,32); | |
//get next '"' | |
for(;;) | |
{ | |
buffer = fgetc(ButtonFile); | |
if(buffer == '"') | |
break; | |
if(buffer == EOF) | |
{ | |
puts("Error: Bad formating in file."); | |
fclose(ButtonFile); | |
exit(EXIT_FAILURE); | |
} | |
} | |
readString(ButtonFile,currentButton->command,256); | |
} | |
else if(buffer == EOF) | |
break; | |
else if(buffer == '#') | |
skipLine(ButtonFile); | |
else if(buffer == '\n'); | |
else if(buffer != ' ' && buffer != '\t') | |
skipLine(ButtonFile); | |
} | |
fclose(ButtonFile); | |
return firstButton; | |
} | |
void drawButtons(Display * display,Window win,GC gc,button * firstButton,int highlighted) | |
{ | |
int i=0; | |
button * currentButton = firstButton; | |
//Lets print all the buttons with a little happy border | |
do | |
{ | |
XSetForeground(display, gc, BORDERCOLOR); | |
XFillRectangle(display, win, gc, 1, 32*i,100,32); | |
if(i == highlighted) | |
{ | |
XSetForeground(display, gc,HIGHLIGHTEDCOLOR); | |
} | |
else | |
{ | |
XSetForeground(display, gc, BUTTONCOLOR); | |
} | |
XFillRectangle( display, win, gc, | |
BORDER, 32 * i + BORDER, | |
100 - 2 * BORDER,32 - 2 * BORDER); | |
XSetForeground(display, gc, TEXTCOLOR); | |
XDrawString ( display, win, gc, 10, 32 * i + 16, | |
currentButton->label, | |
strlen(currentButton->label)); | |
i++; | |
currentButton = currentButton->next; | |
}while(currentButton != NULL); | |
XFlush(display); | |
} | |
void freeButtons(button * first) | |
{ | |
button * currentButton; | |
button * next; | |
currentButton = first; | |
do | |
{ | |
next = currentButton->next; | |
free(currentButton); | |
currentButton = next; | |
}while(currentButton->next != NULL); | |
} | |
void execCommand(char * command) | |
{ | |
system(command); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment