initial release, dirtyest code but works
[multypo.git] / game.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <X11/Xlib.h>
4 #include <X11/extensions/XInput2.h>
5 #include <X11/Xutil.h>
6 #include <unistd.h>
7 #include <limits.h>
8 #include <string>
9 #include <string.h>
10 #include <vector>
11 #include <assert.h>
12 #include <map>
13 #include <iostream>
14 #include <set>
15
16 class Renderable {
17 public:
18         virtual void render (Display*, Window&, GC&) = 0;
19 };
20
21 class TextRenderable : public Renderable {
22 public:
23         int x, y; // position
24         int color; // text color
25         std::string text; // what to render
26         //bool centered; // render centered // TODO
27
28         TextRenderable (int posx, int posy, int textcolor, const char * message)
29          : x(posx), y(posy), color(textcolor), text(message) {}
30
31         virtual void render (Display * dpy, Window & w, GC & gc) {
32                 XSetForeground(dpy, gc, color);
33                 XDrawString(dpy, w, gc, x, y, text.c_str(), text.length());
34         }
35 };
36
37 class Player {
38 private:
39         std::string name;
40         bool isReady;
41         TextRenderable * textField;
42         int indexToType;
43         int score;
44
45 public:
46         Player () : name(), isReady(false), indexToType(0), score(0) {}
47         int getScore () {
48                 return score;
49         }
50         void associateTextField (TextRenderable * t) {
51                 textField = t;
52         }
53         void nameInput (const char * input) {
54                 if (isReady)
55                         return;
56                 if (strlen(input) == 1) {
57                         textField->text.append(input);
58                 } else if (strcmp (input, "Return") == 0) {
59                         name = textField->text;
60                         isReady = true;
61                         textField->text.append(" READY");
62                 } else if (strcmp (input, "BackSpace") == 0 && name.length() > 0) {
63                         textField->text.erase (textField->text.length()-1);
64                 }
65         }
66         // returns if player has just finished the word
67         bool input (const char * input, const std::string & currentWord) {
68                 if (strlen(input) == 1 && currentWord[indexToType] == input[0] && !isReady) {
69                         assert (textField != NULL);
70                         assert (indexToType >= 0);
71                         textField->text.push_back(input[0]);
72                         indexToType++;
73                         if (currentWord.length() == (unsigned int)indexToType) { // done
74                                 indexToType = 0;
75                                 textField->text.erase();
76                                 isReady = true;
77                                 return true;
78                         }
79                 }
80                 return false;
81         }
82         void clearText () {
83                 textField->text.erase();
84                 indexToType = 0;
85         }
86         void addScore (int toAdd) {
87                 assert (toAdd >= 0);
88                 score += toAdd;
89         }
90         void unsetReady () {
91                 isReady = false;
92         }
93         bool getReady () {
94                 return isReady;
95         }
96         const char * getName () {
97                 return name.c_str();
98         }
99 };
100
101 void clearScreen (Display *d, Window& window, GC& gc, int color) {
102         XSetForeground (d, gc, color);
103         unsigned int width,height; // TODO: nicht jedes mal die query machen
104         unsigned int dummyu; int dummyi; Window dummyw;
105         XGetGeometry(d, window, &dummyw, &dummyi, &dummyi, &width, &height, &dummyu, &dummyu);
106         XFillRectangle (d, window, gc, 0, 0, width, height);
107 }
108
109 void render (Display *d, Window& window, GC& gc, int bgcolor, std::vector < Renderable* > & toRender) {
110         clearScreen (d, window, gc, bgcolor);
111         for (size_t i=0; i<toRender.size(); i++) {
112                 toRender[i]->render(d, window, gc);
113         }
114         XFlush (d);
115 }
116
117 int getColorExact (Display *d, int r, int g, int b) {
118         int screen = DefaultScreen(d);
119         Colormap cmap = DefaultColormap(d,screen);
120
121         if (r > 65535) r = 65535;
122         if (g > 65535) g = 65535;
123         if (b > 65535) b = 65535;
124
125         XColor c;
126         c.red = r; c.green = g; c.blue = b;
127
128         if (XAllocColor(d, cmap, &c)) {
129                 if (c.red != r || c.green != g || c.blue != b) {
130                         fprintf (stderr, "(%d,%d,%d) -> (%d,%d,%d)\n", r, g, b, c.red, c.green, c.blue);
131                 }
132                 return c.pixel;
133         } else {
134                 fprintf (stderr, "Warning: couldn't allocate color %d %d %d\n", r, g, b);
135                 return BlackPixel(d,screen);
136         }
137 }
138
139 int getColor (Display *d, int r, int g, int b) {
140         // 0->0, 255->65535
141         return getColorExact (d, r*257, g*257, b*257);
142 }
143
144 std::string getNextWord (bool * isWord_return) {
145         std::string ret;
146         std::getline (std::cin, ret);
147         *isWord_return = !std::cin.eof();
148         return ret;
149 }
150
151 int main (int argc, char** argv) {
152         /* Connect to the X server */
153         Display *dpy = XOpenDisplay(NULL);
154         if (!dpy) {
155                 fprintf (stderr, "XOpenDisplay returned no display to display what is to display. Aborting.\n");
156                 exit (1);
157         }
158
159         /* XInput Extension available? */
160         int opcode, event, error;
161         if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) {
162            printf("X Input extension not available.\n");
163            return -1;
164         }
165
166         /* Which version of XI2? We support 2.0 */
167         int major = 2, minor = 0;
168         if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
169           printf("XI2 not available. Server supports %d.%d\n", major, minor);
170           return -1;
171         }
172         
173         printf ("system is sane.\n");
174         
175         // Get some colors
176         int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
177         int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
178
179         int nplayers=0;
180         int maxplayers=4;
181         TextRenderable playerTextFields [] = {
182                 TextRenderable(0, 200, getColor (dpy, 255, 220,  32), ""),
183                 TextRenderable(0, 300, getColor (dpy,  32, 128, 255), ""),
184                 TextRenderable(0, 400, getColor (dpy,  64, 255,   0), ""),
185                 TextRenderable(0, 500, getColor (dpy, 255,   0,  64), "")
186         };
187
188         // create window
189         Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 800, 600, 0, blackColor, blackColor);
190
191         // which events we want?
192         XSelectInput(dpy, w, StructureNotifyMask);
193
194         // put window on the screen
195         XMapWindow(dpy, w);
196
197         // create graphics context
198         GC gc = XCreateGC(dpy, w, 0, NULL);
199
200         // wait for MapNotify
201         while (true) {
202                 XEvent e;
203                 XNextEvent(dpy, &e);
204                 if (e.type == MapNotify)
205                         break;
206         }
207
208         // Now let's listen to keypress events
209         XIEventMask eventmask;
210         unsigned char mask [1] = { 0 }; // will change
211         eventmask.deviceid = XIAllMasterDevices;
212         eventmask.mask_len = sizeof (mask); // in bytes, for whatever reason...
213         eventmask.mask = mask;
214         XISetMask(mask, XI_KeyPress);
215         XISelectEvents (dpy, w, &eventmask, 1);
216         XSelectInput (dpy, w, ExposureMask);
217
218         // setup player data structures
219         std::map < int , Player* > kbd2player;
220         std::vector< Player* > players;
221
222         // setup rendering stuff
223         std::vector< Renderable* > toRender;
224         for (int i = 0; i < maxplayers; i++)
225                 toRender.push_back (playerTextFields+i);
226         TextRenderable mainTextField (50, 50, whiteColor, "Please type your name and then hit Return.");
227         toRender.push_back (&mainTextField);
228
229         bool allReady = false;
230         while (!allReady) {
231                 render(dpy, w, gc, blackColor, toRender);
232                 XEvent ev;
233                 XNextEvent(dpy, &ev);
234                 if (ev.xcookie.type == GenericEvent && ev.xcookie.extension == opcode
235                     && ev.xcookie.evtype == XI_KeyPress && XGetEventData(dpy, &ev.xcookie)) {
236                     XIDeviceEvent *d_ev = (XIDeviceEvent*) ev.xcookie.data;
237                     KeyCode keycode = d_ev->detail;
238                     int kbdid = d_ev->deviceid;
239                     if (!(d_ev->flags & XIKeyRepeat)) {
240                                 int keysyms_per_keycode;
241                                 //KeySym *keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode);
242                                 KeySym keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode)[0];
243                                 Player * eventPlayer;
244                                 if (kbd2player.count(kbdid) == 0) { // add player
245                                         if (nplayers == maxplayers) {
246                                                 fprintf (stderr, "zu viele Spieler, TODO: gescheites error handling. Bye ;-)\n");
247                                                 exit (1);
248                                         }
249                                         eventPlayer = new Player ();
250                                         eventPlayer->associateTextField (playerTextFields+nplayers);
251                                         nplayers++;
252                                         players.push_back(eventPlayer);
253                                         kbd2player[kbdid] = eventPlayer;
254                                 } else { // player already there
255                                         eventPlayer = kbd2player[kbdid];
256                                 }
257                                 //debug out: mainTextField.text.append (XKeysymToString (keysym[0]));
258                                 eventPlayer->nameInput (XKeysymToString (keysym));
259                         }
260
261                         XFreeEventData(dpy, &ev.xcookie);
262                         if (keycode == 9) {// Escape Key
263                                 printf ("you escaped successfully.\n");
264                                 exit(0);
265                         }
266                 } else if (ev.type == Expose) {
267                         printf ("exposeevent\n");
268                 } else {
269                         printf ("other event\n");
270                         //printf ("else\n");
271                 }
272                 if (!players.empty()) {
273                         allReady = true;
274                         for (size_t i=0; i<players.size() && allReady; i++) {
275                                 allReady &= players[i]->getReady();
276                         }
277                 }
278         }
279
280         for (size_t i=0; i<players.size() && allReady; i++) {
281                 players[i]->clearText();
282                 players[i]->unsetReady();
283         } // readyflag will now be used for if they are already done with a word
284
285         int notreadyplayers = 0;
286         std::string theWord;
287
288         while (true) {
289                 assert (notreadyplayers >= 0);
290                 if (notreadyplayers == 0 || (notreadyplayers==1 && players.size()>1)) {
291                         bool gotWord;
292                         theWord = getNextWord(&gotWord);
293                         if (!gotWord)
294                                 break;
295                         mainTextField.text = theWord;
296                         notreadyplayers = players.size();
297                         for (size_t i=0; i<players.size() && allReady; i++) {
298                                 players[i]->unsetReady();
299                                 players[i]->clearText();
300                                 printf ("cleared text\n");
301                         }
302                 }
303                 render(dpy, w, gc, blackColor, toRender);
304                 XEvent ev;
305                 XNextEvent(dpy, &ev);
306                 if (ev.xcookie.type == GenericEvent && ev.xcookie.extension == opcode
307                     && ev.xcookie.evtype == XI_KeyPress && XGetEventData(dpy, &ev.xcookie)) {
308                     XIDeviceEvent *d_ev = (XIDeviceEvent*) ev.xcookie.data;
309                     KeyCode keycode = d_ev->detail;
310                     int kbdid = d_ev->deviceid;
311                     if (!(d_ev->flags & XIKeyRepeat)) {
312                                 printf ("got key event\n");
313                                 int keysyms_per_keycode;
314                                 //KeySym *keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode);
315                                 KeySym keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode)[0];
316                                 Player * eventPlayer;
317                                 if (kbd2player.count(kbdid) == 0) { // add player
318                                         printf ("you can't join in a match\n");
319                                         exit (1);
320                                         /*if (nplayers == maxplayers) {
321                                                 fprintf (stderr, "zu viele Spieler, TODO: gescheites error handling. Bye ;-)\n");
322                                                 exit (1);
323                                         }
324                                         eventPlayer = new Player ();
325                                         eventPlayer->associateTextField (playerTextFields+nplayers);
326                                         nplayers++;
327                                         players.push_back(eventPlayer);
328                                         kbd2player[srcid] = eventPlayer; */
329                                 } else { // player already there
330                                         eventPlayer = kbd2player[kbdid];
331                                 }
332                                 //debug out: mainTextField.text.append (XKeysymToString (keysym[0]));
333                                 bool pfinished = eventPlayer->input (XKeysymToString (keysym), theWord);
334                                 if (pfinished) {
335                                         notreadyplayers--;
336                                         eventPlayer->addScore (notreadyplayers);
337                                         printf ("score of %d added\n", notreadyplayers);
338                                 }
339                         }
340
341                         XFreeEventData(dpy, &ev.xcookie);
342                         if (keycode == 9) {// Escape Key
343                                 printf ("you escaped successfully.\n");
344                                 exit(0);
345                         }
346                 } else if (ev.type == Expose) {
347                         printf ("exposeevent\n");
348                 } else {
349                         printf ("other event\n");
350                         //printf ("else\n");
351                 }
352         }
353
354         //sleep(10);
355
356         return 0;
357 }