add proper states and a sane API for players
[multypo.git] / qt / multypo.cpp
1 #include "multypo.h"
2
3 #include <QX11Info>
4 #include <QDebug>
5 #include <QVBoxLayout>
6 #include <QLabel>
7 #include <QMap>
8
9 #include <X11/Xlib.h>
10 #include <X11/extensions/XInput2.h>
11 #include <X11/Xutil.h>
12
13 MultypoWindow::MultypoWindow(QWidget *parent) :
14         QWidget(parent),
15     state(Naming),
16         xiInited(false)
17 {
18         /* Prepare colors */
19         setStyleSheet("background-color: black; color: green; font-size: 45pt");
20
21         /* Prepare conents */
22         setLayout(new QVBoxLayout(this));
23
24         mainLabel = new QLabel("Namen eingeben, dann Return", this);
25         layout()->addWidget(mainLabel);
26
27         /* Fullscreen, no cursor */
28         setWindowState(Qt::WindowFullScreen);
29         setCursor(QCursor(Qt::BlankCursor));
30
31         words.open(stdin,QIODevice::ReadOnly);
32 }
33
34 MultypoWindow::~MultypoWindow()
35 {
36 }
37
38 void MultypoWindow::nextWord() {
39         QByteArray tmp = words.readLine().trimmed();
40         currentWord = QString::fromUtf8(tmp);
41         qDebug() << "New word" << currentWord;
42         if (currentWord.isEmpty()) { // game over
43                 mainLabel->setText("GAME OVER");
44                 for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
45                         it.value()->showScore();
46                 }
47                 state = Scoring;
48         } else {
49                 for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
50             it.value()->nextWord();
51         }
52                 mainLabel->setText(currentWord);
53         state = Playing;
54         }
55 }
56
57 bool MultypoWindow::allPlayersWaiting()
58 {
59     for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
60         if (it.value()->getState() != Player::Waiting) return false;
61     }
62     return true;
63 }
64
65 int MultypoWindow::typingPlayers()
66 {
67     int n = 0;
68     for (QMap<int, Player*>::Iterator it = players.begin(); it != players.end(); ++it) {
69         if (it.value()->getState() == Player::Typing) ++n;
70     }
71     return n;
72 }
73
74 void MultypoWindow::handleKeyPress(int device, QString string)
75 {
76         qDebug() << "Device" << device << "String" << string;
77
78         if (string == "Escape") {
79                 close();
80                 return;
81         }
82
83         if (!players.contains(device)) {
84                 if (state > Naming)
85                         return;
86                 players[device] = new Player(this);
87         }
88         Player *player = players[device];
89         player->handleKey(string);
90     
91     if (state == Naming) {
92         // someone's still naming (or nobody's there yet)
93         qDebug() << "checking for game started";
94         if (!players.empty() && allPlayersWaiting()) {
95             nextWord();
96         }
97     }
98     else if (state == Playing) { // all players are waiting or typing
99                 qDebug() << "current player line" << player->getCurrentWord();
100                 qDebug() << "current word" << currentWord;
101                 if (player->getState() == Player::Typing && player->getCurrentWord() == currentWord) {
102             int points = typingPlayers()+1;
103             player->wordComplete(points);
104                         QString readyString = QString("READY: %1 points").arg(points);
105                         if (allPlayersWaiting()) {
106                                 nextWord();
107                         }
108                 }
109         }
110 }
111
112 bool MultypoWindow::handleX11Event(XEvent *event)
113 {
114         Display *dpy = x11Info().display();
115         /* Handle the first map event: We are finally visible! */
116         if (!xiInited && event->type == MapNotify) {
117                 /* XInput Extension available? */
118                 int eventID, errorID;
119                 if (!XQueryExtension(dpy, "XInputExtension", &xiOpcode, &eventID, &errorID)) {
120                    qCritical() << "X Input extension not available";
121                 }
122
123                 /* Which version of XI2? We support 2.0 */
124                 int major = 2, minor = 1;
125                 if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
126                         qCritical() << "XI2 not available. Server supports" << major << "." <<
127                                                 minor;
128                 }
129
130                 qDebug() << "System supports XI2";
131
132
133                 // Now let's listen to keypress events
134                 XIEventMask eventmask;
135                 unsigned char mask [1] = { 0 }; // will change
136                 eventmask.deviceid = XIAllMasterDevices;
137                 eventmask.mask_len = sizeof (mask); // in bytes, for whatever reason...
138                 eventmask.mask = mask;
139                 XISetMask(mask, XI_KeyPress);
140                 XISelectEvents (dpy, winId(), &eventmask, 1);
141
142                 // finally, grab all focuses
143                 int ndevices;
144                 XIDeviceInfo* devices = XIQueryDevice(dpy, XIAllMasterDevices, &ndevices);
145                 for (int i = 0; i < ndevices; ++i) {
146                         if (devices[i].use != XIMasterKeyboard) continue;
147                         qDebug() << "Found master keyboard with ID" << devices[i].deviceid;
148                         XISetFocus(dpy, devices[i].deviceid, winId(), CurrentTime);
149                 }
150                 XIFreeDeviceInfo(devices);
151
152                 xiInited = true;
153
154                 return false;
155         }
156         else if (xiInited && event->type == GenericEvent
157                 && event->xcookie.type == GenericEvent && event->xcookie.extension == xiOpcode
158                 && event->xcookie.evtype == XI_KeyPress) { 
159                 
160                 if (XGetEventData(dpy, &event->xcookie) != True) {
161                         qCritical() << "Error getting event data";
162                         // FIXME return true;
163                 }
164                 
165                 /* Handle XI event */
166                 XIDeviceEvent *d_ev = (XIDeviceEvent*) event->xcookie.data;
167             KeyCode keycode = d_ev->detail;
168             int kbdid = d_ev->deviceid;
169
170                 if (!(d_ev->flags & XIKeyRepeat)) {
171                         int keysyms_per_keycode;
172                         KeySym keysym = XGetKeyboardMapping (dpy, keycode, 1, &keysyms_per_keycode)[0];
173                         handleKeyPress(kbdid, XKeysymToString(keysym));
174                 }
175
176                 XFreeEventData(dpy, &event->xcookie);
177
178                 return true;
179         }
180         else {
181                 return false;
182         }
183 }