Initial import for the first release.
[wllib.git] / wllib-X11.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <signal.h>
5 #include <unistd.h>
6 #include <math.h>
7 #include <ctype.h>
8 #include <limits.h>
9 #include <assert.h>
10 #include <X11/X.h>
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <X11/Xresource.h>
14 #include <X11/xpm.h>
15 #include <X11/extensions/shape.h>
16 #include <X11/cursorfont.h>
17 #include "wllib.h"
18
19 #define SAFE_BORDER (4)
20 #define SIZE_MIN (16)
21
22 extern char *icon_xpm[];
23 extern void drawgfx(void);
24
25 long xcur,ycur,xmin,xmax,ymin,ymax;
26 int winx,winy,iniline,minimax=1;
27 float coefx,coefy;
28
29 Display *dp;
30 Screen *scr;
31 int scrnum;
32 Colormap cmap;
33 unsigned long fgpen,bgpen;
34 XrmDatabase rdb;
35 Window win,rootw;
36 GC gc;
37 Cursor curs;
38 Atom atdel;
39
40 XrmOptionDescRec opts[]={
41 {"-bg",          ".background",  XrmoptionSepArg,NULL}, /*  0! */
42 {"-background",  ".background",  XrmoptionSepArg,NULL}, /*  1! */
43 {"-bd",          ".borderColor", XrmoptionSepArg,NULL}, /*  2! */
44 {"-bordercolor", ".borderColor", XrmoptionSepArg,NULL}, /*  3! */
45 {"-bw",          ".borderWidth", XrmoptionSepArg,NULL}, /*  4! */
46 {"-borderwidth", ".borderWidth", XrmoptionSepArg,NULL}, /*  5! */
47 {"-display",     ".display",     XrmoptionSepArg,NULL}, /*  6! */
48 {"-fg",          ".foreground",  XrmoptionSepArg,NULL}, /*  7! */
49 {"-foreground",  ".foreground",  XrmoptionSepArg,NULL}, /*  8! */
50 {"-geometry",    ".geometry",    XrmoptionSepArg,NULL}, /*  9! */
51 {"-iconic",      ".iconStartup", XrmoptionNoArg, "on"}, /* 10! */
52 {"-name",        ".name",        XrmoptionSepArg,NULL}, /* 11! */
53 {"-rv",          ".reverseVideo",XrmoptionNoArg, "on"}, /* 12! */
54 {"-reverse",     ".reverseVideo",XrmoptionNoArg, "on"}, /* 13! */
55 {"+rv",          ".reverseVideo",XrmoptionNoArg, "on"}, /* 14! */
56 {"+reverse",     ".reverseVideo",XrmoptionNoArg, "on"}, /* 15! */
57 {"-title",       ".title",       XrmoptionSepArg,NULL}, /* 16! */
58 {"-xrm",         NULL,           XrmoptionResArg,NULL}, /* 17! */
59 };
60 #define opts_num (sizeof(opts)/sizeof(*opts))
61
62 char *pname;
63 size_t pnamelen;
64 char fn[128]="/usr/lib/X11/app-defaults/",resnam[sizeof(fn)],rescls[sizeof(fn)];
65 #define APPDEFNL (26)
66 size_t classlen;
67
68 void wl_line(long x,long y)
69 {
70         if (iniline) {
71                 iniline=0;
72                 if (minimax) { xmin=xmax=x; ymin=ymax=y; }
73                 else {
74                         xcur=rint(coefx*(x-xmin))+SAFE_BORDER;
75                         ycur=winy-SAFE_BORDER-rint(coefy*(y-ymin));
76                         }
77                 }
78         else {
79                 if (minimax) {
80                         if (x<xmin) xmin=x; if (x>xmax) xmax=x;
81                         if (y<ymin) ymin=y; if (y>ymax) ymax=y;
82                         }
83                 else {
84 int xcur2,ycur2;
85                         XDrawLine(dp,win,gc,xcur,ycur,
86                                         xcur2=rint(coefx*(x-xmin))+SAFE_BORDER,ycur2=winy-SAFE_BORDER-rint(coefy*(y-ymin)));
87                         xcur=xcur2; ycur=ycur2;
88                         }
89                 }
90 }
91
92 int stricmp(char *s1,char *s2)
93 {
94 int i;
95 char c1,c2;
96         while (*s1&&*s2) if ((i=(c1=toupper(*s1++))>(c2=toupper(*s2++))-(c1<c2))) return(i);
97         return(0);
98 }
99
100 static char *res_string(int resn,const char *resc)
101 {
102 char *s;
103 XrmValue val;
104         strncpy(&resnam[pnamelen],opts[resn].specifier,sizeof(resnam)-pnamelen);
105         strncpy(&rescls[classlen],resc                ,sizeof(rescls)-classlen);
106         if (XrmGetResource(rdb,resnam,rescls,&s,&val)) {
107                 if (!(s=malloc(val.size))) iofail();
108                 return(strncpy(s,val.addr,val.size));
109                 }
110         return(NULL);
111 }
112
113 static int res_bool(char *s)
114 {
115 static char *yestr[4]={"1","on","yes","true"};
116 #define YESTRL (sizeof(yestr)/sizeof(*yestr))
117 int i;
118         if (s) for (i=0;i<YESTRL;i++)
119                 if (!stricmp(s,yestr[i])) { free(s); return(1); }
120         free(s);
121         return(0);
122 }
123
124 static int res_int(char *s,int def)
125 {
126 char *ers;
127         if (s) {
128                 def=strtol(s,&ers,0);
129                 if (ers&&*ers) { fprintf(stderr,"strtol() error @\"%s\"\n",ers); iofail(); }
130                 free(s);
131                 }
132         return(def);
133 }
134
135 typedef enum { color_def_White,color_def_Black } color_def;
136 static unsigned long get_color(char *desc,color_def def)
137 {
138 XColor xcol_scr,xcol_ex;
139         if (!desc) return(def==color_def_White?WhitePixelOfScreen(scr):BlackPixelOfScreen(scr));
140         else if (!XAllocNamedColor(dp,cmap=DefaultColormapOfScreen(scr),desc,&xcol_scr,&xcol_ex))
141                         { fprintf(stderr,"XAllocNamedColor(\"%s\")\n",desc); iofail(); }
142         return(xcol_scr.pixel);
143 }
144
145 void wl_done(void)
146 {
147 XEvent ev;
148         iniline=1; drawgfx();
149         minimax=0;
150         if (xmax==xmin) xmax++;
151         if (ymax==ymin) ymax++;
152         XUndefineCursor(dp,win);
153         XFreeCursor(dp,curs);
154         for (;;) {
155                 XNextEvent(dp,&ev);
156                 if (ev.xany.window==win) switch(ev.type) {
157                         case ConfigureNotify:
158                                 winx=ev.xconfigure.width; winy=ev.xconfigure.height;
159                                 break;
160                         case ClientMessage:
161                                 if (ev.xclient.format==32) if (ev.xclient.data.l[0]==atdel)
162                         case ButtonPress:
163                         case KeyPress:
164                                         exit(EXIT_SUCCESS);
165                                 break;
166                         case Expose:
167                                 XClearArea(dp,win,ev.xexpose.x,ev.xexpose.y,ev.xexpose.width,ev.xexpose.height,False);
168                                 if (ev.xexpose.count) break;
169                                 if (!iniline) {
170                                         coefx=(float)(winx-1-2*SAFE_BORDER)/(xmax-xmin);
171                                         coefy=(float)(winy-1-2*SAFE_BORDER)/(ymax-ymin);
172                                         iniline=1; drawgfx();
173                                         }
174                         }
175                 }
176 }
177
178 void wl_init(int *argcp,char **argv,const char *class)
179 {
180 char *s,*disp;
181 size_t ts;
182 unsigned long bdpen;
183 unsigned bw;
184 XSetWindowAttributes wina;
185 XSizeHints *sizh;
186 XWMHints *wmh;
187 XClassHint *clsh;
188 int xs,ys,i;
189 struct {
190         Pixmap data,mask;
191         XpmAttributes attr;
192         Window win;
193         XIconSize *siz;
194         } icon;
195
196         if ((pname=rindex(*argv,'/'))) pname++;
197         else pname=*argv;
198         pnamelen=strlen(pname);
199         XrmInitialize();
200         strncpy(&fn[APPDEFNL],class,sizeof(fn)-APPDEFNL);
201         rdb=XrmGetFileDatabase(fn);
202         if (!(s=getenv("XUSERFILESEARCHPATH"))) s=getenv("XAPPLRESDIR");
203         if (s) if (!(ts=strlen(strncpy(fn,s,sizeof(fn))))) {
204                 if (fn[ts-1]!='/') fn[ts++]='/';
205                 strncpy(&fn[ts],class,sizeof(fn)-ts);
206                 XrmCombineFileDatabase(fn,&rdb,True);
207                 }
208         XrmParseCommand(&rdb,opts,opts_num,class,argcp,argv);
209         strncpy(resnam,pname,sizeof(resnam));
210         strncpy(rescls,class,sizeof(rescls));
211         classlen=strlen(class);
212         if ((s=res_string(11,".Name")))
213                 pnamelen=strlen(pname=s);
214         if (!(dp=XOpenDisplay(disp=res_string(6,".Display"))))
215                 { fprintf(stderr,"Display=\"%s\"\n",XDisplayName(disp)); iofail(); }
216         if (!(s=getenv("HOME"))) s="~";
217         ts=strlen(strncpy(fn,s,sizeof(fn)));
218         strncpy(&fn[ts],"/.Xdefaults-",sizeof(fn)-ts); ts+=12;
219         if (!(s=getenv("XENVIRONMENT"))) {
220                 if (gethostname(&fn[ts],sizeof(fn)-ts)) iofail();
221                 s=fn;
222                 }
223         XrmCombineFileDatabase(s,&rdb,False);
224         if ((s=XScreenResourceString(scr=ScreenOfDisplay(dp,scrnum=DefaultScreen(dp)))))
225                 XrmCombineDatabase(XrmGetStringDatabase(s),&rdb,False);
226         if ((s=XResourceManagerString(dp)))
227                 XrmCombineDatabase(XrmGetStringDatabase(s),&rdb,False);
228         else {
229                 fn[ts-1]='\0';
230                 XrmCombineFileDatabase(fn,&rdb,False);
231                 }
232         bw=res_int(res_string(4,".BorderWidth"),0);
233         bgpen=get_color(res_string(0,".Background" ),color_def_White);
234         fgpen=get_color(res_string(7,".Foreground" ),color_def_Black);
235         if (res_bool(res_string(12,".ReverseVideo")))
236                 { bdpen=bgpen; bgpen=fgpen; fgpen=bdpen; }
237         bdpen=get_color(res_string(2,".BorderColor"),color_def_Black);
238         if (!(sizh=XAllocSizeHints())) iofail();
239         xs=WidthOfScreen(scr)/2; ys=HeightOfScreen(scr)/2;
240         sprintf(fn,"%dx%d+%d+%d",xs,ys,xs/2,ys/2);
241         sizh->flags=(i=XWMGeometry(dp,scrnum,res_string(9,".Geometry"),fn,bw,sizh,
242                                         &sizh->x,&sizh->y,&winx,&winy,&sizh->win_gravity)
243                         &(XValue|YValue)?USPosition:PPosition)|(i&(WidthValue|HeightValue)?USSize:PSize)
244                         |PMinSize|PWinGravity;
245         sizh->min_height=sizh->min_width=2*SAFE_BORDER+SIZE_MIN;
246         if (!(win=XCreateSimpleWindow(dp,rootw=RootWindowOfScreen(scr),
247                         sizh->x,sizh->y,sizh->width=winx,sizh->height=winy,bw,bdpen,bgpen))) iofail();
248         icon.attr.valuemask=0;
249         if ((i=XpmCreatePixmapFromData(dp,win,icon_xpm,&icon.data,&icon.mask,&icon.attr)))
250                 { fprintf(stderr,"XpmCreatePixmapFromData() error #%d\n",i); iofail(); }
251         if (!(icon.siz=XAllocIconSize())) iofail();
252         icon.siz->max_width =icon.siz->min_width =icon.attr.width;
253         icon.siz->max_height=icon.siz->min_height=icon.attr.height;
254         icon.siz->height_inc=icon.siz->width_inc=0;
255         XSetIconSizes(dp,win,icon.siz,1);
256         XFree(icon.siz);
257         if (!(icon.win=XCreateSimpleWindow(dp,rootw,0,0,
258                         icon.attr.width,icon.attr.height,0,bdpen,bgpen))) iofail();
259         XpmFreeAttributes(&icon.attr);
260         XSetWindowBackgroundPixmap(dp,icon.win,icon.data);
261         XFreePixmap(dp,icon.data);
262         if (icon.mask) {
263                 XShapeCombineMask(dp,icon.win,ShapeBounding,0,0,icon.mask,ShapeSet);
264                 XFreePixmap(dp,icon.mask);
265                 }
266         XClearWindow(dp,icon.win);
267         if (!(wmh=XAllocWMHints())) iofail();
268         wmh->flags=StateHint|IconWindowHint;
269         wmh->initial_state=res_bool(res_string(10,".IconStartup"))?IconicState:NormalState;
270         wmh->icon_window=icon.win;
271         if (!(clsh=XAllocClassHint())) iofail();
272         clsh->res_name=pname;
273         clsh->res_class=(char *)class;
274         if (!(s=res_string(16,".Title"))) s=(char *)class;
275         XmbSetWMProperties(dp,win,s,class,argv,*argcp,sizh,wmh,clsh);
276         XFree(sizh); XFree(wmh); XFree(clsh);
277         wina.backing_store=WhenMapped;
278         wina.event_mask=ButtonPressMask|KeyPressMask|StructureNotifyMask|ExposureMask;
279         wina.cursor=curs=XCreateFontCursor(dp,XC_watch);
280         XChangeWindowAttributes(dp,win,CWBackingStore|CWEventMask|CWCursor,&wina);
281         atdel=XInternAtom(dp,"WM_DELETE_WINDOW",False);
282         XSetWMProtocols(dp,win,&atdel,1);
283         XMapWindow(dp,win);
284         XFlush(dp);
285         gc=DefaultGCOfScreen(scr);
286 }