fix agentOnCamera to cope with the wrap
[openc2e.git] / qtgui / QtBackend.cpp
blob173c550dac4e1626f5c394a755c820990d2e7812
1 /*
2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation; either version 2 of the License, or
5 (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License along
13 with this program; if not, write to the Free Software Foundation, Inc.,
14 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 #include "QtBackend.h"
18 #include "qtopenc2e.h"
19 #include "Engine.h"
20 #include <QKeyEvent>
21 #include <QApplication>
22 #include <QWidget>
23 #include <QPainter>
24 #include <boost/format.hpp>
25 #include <iostream>
26 #include "exceptions.h"
28 #ifdef _WIN32
29 #include <windows.h>
30 #endif
32 #ifdef _WIN32
33 HBITMAP screen_bmp = 0;
34 void *oldPixels = 0;
35 #endif
37 QtBackend::QtBackend() {
38 viewport = 0;
39 needsrender = false;
41 for (unsigned int i = 0; i < 256; i++) {
42 downkeys[i] = false;
46 void QtBackend::shutdown() {
47 if (!viewport) return;
49 #ifdef _WIN32
50 if (screen_bmp) DeleteObject(screen_bmp);
52 SDL_Surface *surf = getMainSDLSurface();
53 if (surf) surf->pixels = oldPixels;
54 #endif
56 SDLBackend::shutdown();
59 void QtBackend::init() {
62 void QtBackend::setup(QWidget *vp) {
63 viewport = vp;
65 #if defined(Q_WS_X11)
66 // on X11, using SDL_WINDOWID works, thank goodness.
67 static char windowid_str[64]; // possibly big, but who cares
68 ((QApplication *)QApplication::instance())->syncX();
70 if (sizeof windowid_str <=
71 (size_t)snprintf(windowid_str, sizeof windowid_str, "SDL_WINDOWID=0x%lx", viewport->winId())) {
72 std::cerr << "windowid_str buffer was too small - how big are your longs, anyway? Panicing..." << std::endl;
73 abort();
75 // ->winId() may have created the window for us
76 ((QApplication *)QApplication::instance())->syncX();
77 putenv(windowid_str);
78 #else
79 // alas, it sucks on Windows and OS X, so we use an offscreen buffer instead
80 putenv("SDL_VIDEODRIVER=dummy");
81 #endif
83 SDLBackend::init();
86 char videodrivername[200];
87 std::cout << "SDL video driver: " << SDL_VideoDriverName(videodrivername, 200) << std::endl;
88 std::cout << "SDL requested colour depth: " << idealBpp() << std::endl;
91 viewport->setCursor(Qt::BlankCursor);
94 int QtBackend::idealBpp() {
95 #if defined(Q_WS_X11)
96 return SDLBackend::idealBpp();
97 #endif
99 // TODO: handle 8bpp for C1
101 #ifdef _WIN32
102 // TODO: how to pick up real depth on windows?
103 if (viewport->depth() == 16) return 16;
104 return 24;
105 #endif
107 return 32;
111 void QtBackend::resized(int w, int h) {
112 resizeNotify(w, h);
114 #ifdef _WIN32
115 // We need to construct a DIB for blitting, based on the surface data.
117 // Delete the old one if needed.
118 if (screen_bmp) DeleteObject(screen_bmp);
120 // Setup a BITMAPINFO structure for this.
121 BITMAPINFO *binfo = (BITMAPINFO *)malloc(sizeof(BITMAPINFO) + 12); // TODO: wrong wrong wrong
122 binfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
123 binfo->bmiHeader.biPlanes = 1;
124 binfo->bmiHeader.biClrUsed = 0;
125 binfo->bmiHeader.biClrImportant = 0;
127 binfo->bmiHeader.biXPelsPerMeter = 0;
128 binfo->bmiHeader.biYPelsPerMeter = 0;
130 SDL_Surface *surf = getMainSDLSurface();
131 assert(idealBpp() == surf->format->BitsPerPixel);
133 // Set the relevant entries of the structure.
134 binfo->bmiHeader.biWidth = w;
135 binfo->bmiHeader.biHeight = -h;
136 binfo->bmiHeader.biSizeImage = w * h * (idealBpp() / 8);
137 binfo->bmiHeader.biBitCount = idealBpp();
139 // Describe the format of the data and any additional information needed (eg masks/palette)
140 if (idealBpp() == 16) {
141 binfo->bmiHeader.biCompression = BI_BITFIELDS;
142 unsigned int *masks = (unsigned int *)binfo->bmiColors;
143 masks[0] = surf->format->Rmask;
144 masks[1] = surf->format->Gmask;
145 masks[2] = surf->format->Bmask;
146 } else {
147 binfo->bmiHeader.biCompression = BI_RGB;
148 if (idealBpp() == 8) {
149 // TODO: set binfo.bmiColors
153 // Create the actual DIB.
154 void *pixels = 0;
155 HDC hdc = GetDC(viewport->winId());
156 screen_bmp = CreateDIBSection(hdc, binfo, DIB_RGB_COLORS, (void **)(&pixels), NULL, 0);
157 ReleaseDC(viewport->winId(), hdc);
159 // Free the BITMAPINFO structure now we're done with it.
160 free(binfo);
162 // TODO: fall back to Qt?
163 if (!screen_bmp || !pixels) {
164 // Windows helpfully provides no useful error information :(
165 throw creaturesException("Internal error: failed to create DIB");
168 // TODO: Observe how this helpfully stomps over surf->pixels. :-/
169 oldPixels = surf->pixels; // store so we can restore before SDL shutdown
170 surf->pixels = pixels;
172 // So, CreateDIBSection doesn't pay a lot of attention to what we ask for.
173 // Let's snaffle the information back from the DIB object.
174 // TODO: Observe how this helpfully stomps over surf->w/h/pitch. :-/
175 BITMAP dibsection;
176 GetObject(screen_bmp, sizeof(BITMAP), &dibsection);
177 surf->w = dibsection.bmWidth;
178 surf->h = dibsection.bmHeight;
179 surf->pitch = dibsection.bmWidthBytes;
180 assert(dibsection.bmBitsPixel == surf->format->BitsPerPixel);
182 #endif
184 // add resize window event to backend queue
185 SomeEvent e;
186 e.type = eventresizewindow;
187 e.x = w;
188 e.y = h;
189 pushEvent(e);
192 void QtBackend::renderDone() {
193 needsrender = false;
195 // If we're not on X11, we need to copy the contents of the offscreen buffer into the window.
197 #if defined(_WIN32)
198 // We need to blit from the DIB we made earlier. Easy!
199 HDC hdc, mdc;
200 SDL_Surface *surf = getMainSDLSurface(); // for width/height
202 // Obtain the DC for our viewport and create a compatible one, then select the DIB into it.
203 hdc = GetDC(viewport->winId());
204 mdc = CreateCompatibleDC(hdc);
205 SelectObject(mdc, screen_bmp);
207 // Blit!
208 BitBlt(hdc, 0, 0, surf->w, surf->h, mdc, 0, 0, SRCCOPY);
210 // Tidy up by deleting our temporary one and releasing the viewport DC.
211 DeleteDC(mdc);
212 ReleaseDC(viewport->winId(), hdc);
214 // TODO: call GdiFlush? SDL doesn't seem to bother.
216 #elif !defined(Q_WS_X11)
217 // As a generic method, we use Qt's code.
218 // Note that we don't bother to lock because we know the dummy driver doesn't bother with locking.
220 SDL_Surface *surf = getMainSDLSurface();
221 QImage img((uchar *)surf->pixels, surf->w, surf->h, QImage::Format_RGB32);
222 QPainter painter(viewport);
223 painter.drawImage(0, 0, img);
224 #endif
227 bool QtBackend::pollEvent(SomeEvent &e) {
228 // obtain events from backend
229 if (SDLBackend::pollEvent(e)) return true;
231 if (events.size() == 0)
232 return false;
234 e = events.front();
235 events.pop_front();
236 return true;
239 void QtBackend::pushEvent(SomeEvent e) {
240 events.push_back(e);
243 int translateQtKey(int qtkey) {
244 if (qtkey >= Qt::Key_F1 && qtkey <= Qt::Key_F12) {
245 return 112 + (qtkey - Qt::Key_F1);
248 switch (qtkey) {
249 case Qt::Key_Backspace: return 8;
250 case Qt::Key_Tab: return 9;
251 case Qt::Key_Clear: return 12;
252 case Qt::Key_Return: return 13;
253 case Qt::Key_Enter: return 13;
254 case Qt::Key_Shift: return 16;
255 case Qt::Key_Control: return 17;
256 case Qt::Key_Pause: return 19;
257 case Qt::Key_CapsLock: return 20;
258 case Qt::Key_Escape: return 27;
259 case Qt::Key_PageUp: return 33;
260 case Qt::Key_PageDown: return 34;
261 case Qt::Key_End: return 35;
262 case Qt::Key_Home: return 36;
263 case Qt::Key_Left: return 37;
264 case Qt::Key_Up: return 38;
265 case Qt::Key_Right: return 39;
266 case Qt::Key_Down: return 40;
267 case Qt::Key_Print: return 42;
268 case Qt::Key_Insert: return 45;
269 case Qt::Key_Delete: return 46;
270 case Qt::Key_NumLock: return 144;
271 default: return -1;
275 void QtBackend::keyEvent(QKeyEvent *k, bool pressed) {
276 int translatedkey = translateQtKey(k->key());
277 int key = translatedkey;
278 if (key == -1) {
279 if (k->key() >= Qt::Key_0 && k->key() <= Qt::Key_9) {
280 key = k->key();
281 } else if (k->key() >= Qt::Key_A && k->key() <= Qt::Key_Z) {
282 key = k->key();
285 if (key != -1) {
286 SomeEvent e;
287 if (pressed)
288 e.type = eventspecialkeydown;
289 else
290 e.type = eventspecialkeyup;
291 e.key = key;
292 pushEvent(e);
293 downkeys[key] = pressed;
294 if (translatedkey != -1) return;
297 for (int i = 0; i < k->text().size(); i++) {
298 // TODO: openc2e probably doesn't like latin1
299 unsigned char x = k->text().at(i).toLatin1();
300 if (x > 31) { // Qt helpfully hands us non-text chars for some crazy reason
301 // We have a Latin-1 key which we can process.
302 SomeEvent e;
304 // the engine only handles eventkeydown at present
305 if (pressed) {
306 e.type = eventkeydown;
307 e.key = x;
308 pushEvent(e);
314 bool QtBackend::keyDown(int key) {
315 return downkeys[key];
318 int QtBackend::run(int argc, char **argv) {
319 QApplication app(argc, argv);
320 boost::shared_ptr<QtBackend> qtbackend = boost::dynamic_pointer_cast<class QtBackend, class Backend>(engine.backend);
321 assert(qtbackend.get() == this);
323 QtOpenc2e myvat(qtbackend);
324 myvat.show();
326 return app.exec();