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