Skip to content

Instantly share code, notes, and snippets.

@antoineMoPa
Created March 30, 2013 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antoineMoPa/5277081 to your computer and use it in GitHub Desktop.
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.
// main.c
//
// Copyright 2012 Antoine Morin-Paulhus <antoine.morin.paulhus@gmail.com>
//
// 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