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