summaryrefslogtreecommitdiffstats
path: root/progs/miniglx/sample_server2.c
blob: 58effcf484d494975122514cfac14316fec638cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/* $Id: sample_server2.c,v 1.2 2003/08/23 01:28:59 jonsmirl Exp $ */

/*
 * Sample server that just keeps first available window mapped.
 * 
 * It also reads and echos anything that happens on stdin as an
 * example of tracking events from sources other than miniglx clients.
 * 
 * It reads & writes without blocking, so that eg. piping a lot of
 * text to stdin and then hitting 'ctrl-S' on the output stream won't
 * cause it to stop handling miniglx events.
 *
 * See select_tut in the linux manual pages for a good overview of the 
 * select(2) system call.
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <GL/gl.h>
#include <GL/miniglx.h>
#include <errno.h>
#include <assert.h>

struct client {
   struct client *next;
   Window windowid;
   int mappable;
};

struct client *clients = 0, *mapped_client = 0;

#define BUFSZ 4096
char rbuf[BUFSZ];
int rbuf_count;


static struct client *find_client( Window id )
{
   struct client *c;

   for (c = clients ; c ; c = c->next)
      if (c->windowid == id)
	 return c;

   return 0;
}

int main( int argc, char *argv[] )
{
   Display *dpy;
   XEvent ev;
   int autostart = 0;

   if (argc == 2 && strcmp(argv[1], "-autostart") == 0)
      autostart = 1;

   dpy = __miniglx_StartServer(NULL);
   if (!dpy) {
      fprintf(stderr, "Error: __miniglx_StartServer failed\n");
      return 1;
   }

   /* How is vt switching communicated through the XNextEvent interface?
    */
   while (1) {
      int r, n;
      struct timeval tv;
      fd_set rfds, wfds;
      int bored = 0;

      FD_ZERO(&rfds);
      FD_ZERO(&wfds);
      tv.tv_sec = 1;
      tv.tv_usec = 0;

      if (rbuf_count) {
	 FD_SET( 1, &wfds );	/* notify when we can write out buffer */
	 n = 1;
      }
      else {
	 FD_SET( 0, &rfds );	/* else notify when new data to read */
	 n = 0;
      }

      /* __miniglx_Select waits until any of these file groups becomes
       * readable/writable/etc (like regular select), until timeout
       * expires (like regular select), until a signal is received
       * (like regular select) or until an event is available for
       * XCheckMaskEvent().
       */
      r = __miniglx_Select( dpy, n+1, &rfds, &wfds, 0, &tv );

      /* This can happen if select() is interrupted by a signal:
       */
      if (r < 0 && errno != EINTR && errno != EAGAIN) {
	 perror ("select()");
	 exit (1);
      }

      if (tv.tv_sec == 0 && tv.tv_usec == 0)
	 bored = 1;

      /* Check and handle events on our local file descriptors
       */
      if (FD_ISSET( 0, &rfds )) {
	 /* Something on stdin */	 
	 assert(rbuf_count == 0);
	 r = read(0, rbuf, BUFSZ);	 
	 if (r < 1) {
	    perror("read");	
	    abort();
	 }
	 rbuf_count = r;
      }

      if (FD_ISSET( 1, &wfds )) {
	 /* Can write to stdout */
	 assert(rbuf_count > 0);
	 r = write(1, rbuf, rbuf_count);	 
	 if (r < 1) {
	    perror("write");
	    abort();
	 }
	 rbuf_count -= r;
	 if (rbuf_count) 
	    memmove(rbuf + r, rbuf, rbuf_count);
      }


      /* Check and handle events generated by miniglx:
       */
      while (XCheckMaskEvent( dpy, ~0, &ev )) {
	 struct client *c;
	 bored = 0;

	 fprintf(stderr, "Received event %d\n", ev.type);

	 switch (ev.type) {
	 case CreateNotify: 
	    fprintf(stderr, "CreateNotify -- new client\n");
	    c = malloc(sizeof(*c));
	    c->next = clients;
	    c->windowid = ev.xcreatewindow.window;
	    c->mappable = False;
	    clients = c;
	    break;

	 case DestroyNotify:
	    fprintf(stderr, "DestroyNotify\n");
	    c = find_client(ev.xdestroywindow.window);
	    if (!c) break;
	    if (c == clients)
	       clients = c->next;
	    else {
	       struct client *t;
	       for (t = clients ; t->next != c ; t = t->next)
		  ;
	       t->next = c->next;
	    }

	    if (c == mapped_client) 
	       mapped_client = 0;

	    free(c);
	    break;

	 case MapRequest:
	    fprintf(stderr, "MapRequest\n");
	    c = find_client(ev.xmaprequest.window);
	    if (!c) break;
	    c->mappable = True;
	    break;

	 case UnmapNotify:
	    fprintf(stderr, "UnmapNotify\n");
	    c = find_client(ev.xunmap.window);
	    if (!c) break;
	    c->mappable = False;
	    if (c == mapped_client)
	       mapped_client = 0;
	    break;

	 default:
	    break;
	 }
      }


      /* Search for first mappable client if none already mapped.
       */
      if (!mapped_client) {
	 struct client *c;
	 for (c = clients ; c ; c = c->next) {
	    if (c->mappable) {
	       XMapWindow( dpy, c->windowid );
	       mapped_client = c;
	       break;
	    }
	 }
	 if (!clients && autostart) {
	    system("nohup ./texline &");
	    system("nohup ./manytex &");
	 }
      }
      else if (bored) {
	 struct client *c;
	 /* bored of mapped client now, let's try & find another one */
	 for (c = mapped_client->next ; c && !c->mappable ; c = c->next)
	    ;
	 if (!c)
	    for (c = clients ; c && !c->mappable ; c = c->next)
	       ;
	 if (c && c != mapped_client) {
	    XUnmapWindow( dpy, mapped_client->windowid );
	    XMapWindow( dpy, c->windowid );
	    mapped_client = c;
	 }
	 else 
	    fprintf(stderr, "I'm bored!\n");
      }
   }

   XCloseDisplay( dpy );

   return 0;
}