gemw32window.cpp 16.7 KB
Newer Older
zmoelnig's avatar
zmoelnig committed
1
2
3
4
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
5
// zmoelnig@iem.at
zmoelnig's avatar
zmoelnig committed
6
7
8
9
//
// Implementation file
//
//    Copyright (c) 1997-1999 Mark Danks.
zmoelnig's avatar
zmoelnig committed
10
//    Copyright (c) 2001-2011 IOhannes m zmölnig. forum::für::umläute. IEM. zmoelnig@iem.at
zmoelnig's avatar
zmoelnig committed
11
12
13
14
15
//
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
16
#include "Gem/GemConfig.h"
zmoelnig's avatar
zmoelnig committed
17
#ifdef _WIN32
18
19
20
21
// disable QuickTime support in here (where it is not used)
// if a plugin requires QuickTime to be initialized, it has to do it itself...
# undef HAVE_QUICKTIME

IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
22
# define GEMW32WINDOW_INTERNAL
zmoelnig's avatar
zmoelnig committed
23
24
# include "gemw32window.h"

25
26
#define DEBUGLINE ::startpost("%s:%d[%s] ", __FILE__, __LINE__, __FUNCTION__), ::post

zmoelnig's avatar
zmoelnig committed
27
28
29
# include <stdlib.h>

# ifdef HAVE_QUICKTIME
30
31
32
33
34
35
36
37
38
#  if defined __MINGW32__
/* hack to avoid the use of microsofts non-standard extension (u)i64 instead of
 * (U)LL */
#   include <ConditionalMacros.h>
#   undef TARGET_OS_WIN32
#   include <Math64.h>
#   define TARGET_OS_WIN32 1
#  endif /* MINGW */

zmoelnig's avatar
zmoelnig committed
39
40
41
42
#  include <QTML.h>
#  include <Movies.h>
# endif /* HAVE_QUICKTIME */

43
44
# include "Gem/Event.h"
# include "Gem/GemGL.h"
45
# include "RTE/MessageCallbacks.h"
46
# include "Gem/Exception.h"
zmoelnig's avatar
zmoelnig committed
47

48
# include <map>
zmoelnig's avatar
zmoelnig committed
49

50
51
static bool initGemWin(void)
{
zmoelnig's avatar
zmoelnig committed
52
# ifdef HAVE_QUICKTIME
53
  OSErr           err = noErr;
zmoelnig's avatar
zmoelnig committed
54

55
56
57
58
59
60
61
62
63
64
65
66
67
  // Initialize QuickTime Media Layer
  err = InitializeQTML(0);
  if (err) {
    error("GEM Man: Could not initialize quicktime: error %d\n", err);
    return false;
  }
  // Initialize QuickTime
  err = EnterMovies();
  if (err) {
    error("GEM Man: Could not initialize quicktime: error %d\n", err);
    return false;
  }
  verbose(1, "Gem Man: QT init OK");
zmoelnig's avatar
zmoelnig committed
68
69
70
71
72
# endif /* HAVE_QUICKTIME */
  return true;
}


73
74
class gemw32window :: Window
{
75
76
77
78
79
80
public:
  HWND win;
  HDC dc;
  HGLRC context;

  static HGLRC sharedContext;
81
  Window(gemw32window*parent, HINSTANCE hInstance, int buffer,
82
         bool fullscreen, bool border, const std::string&title, int &x, int &y,
83
         unsigned int &w, unsigned int &h) :
84
85
    win(NULL),
    dc(NULL),
86
87
    context(NULL)
  {
88
89
    if(fullscreen)
      border = false;
90
    try {
91
      create(hInstance, buffer, fullscreen, border, title, x, y, w, h);
92
93
94
95
    } catch(GemException&x) {
      destroy();
      throw(x);
    }
96
97
98
    if(parent && win) {
      s_winmap[win]=parent;
    }
99
  }
100
101
  ~Window(void)
  {
102
103
    destroy();
  }
104
105
  static RECT getRealRect(int x, int y, unsigned int w, unsigned int h,
                          bool border, bool fullscreen,
106
107
108
109
                          DWORD &style, DWORD&exStyle)
  {
    exStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    style  =WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
110

111
112
113
114
115
116
117
    if (fullscreen) {
      exStyle  = WS_EX_APPWINDOW;
      style     |= WS_POPUP;
    } else {
      exStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
      if (border) {
        style |= WS_OVERLAPPEDWINDOW;
118
      } else {
119
        style |= WS_POPUP;
120
      }
121
    }
122

123
124
125
126
127
    RECT newSize;
    newSize.left = x;
    newSize.top  = y;
    newSize.right  = w+x;
    newSize.bottom = h+y;
128

129
130
131
132
133
134
135
136
137
138
    AdjustWindowRectEx(&newSize, style, FALSE, exStyle); // no menu
    if (newSize.left<0 && x>=0) {
      newSize.right-=newSize.left;
      newSize.left=0;
    }
    if (newSize.top<0 && y>=0) {
      newSize.bottom-=newSize.top;
      newSize.top=0;
    }
    return newSize;
139
  }
140
141

private:
142
  static std::map<HWND, gemw32window*>s_winmap;
143
  void create(HINSTANCE hInstance, int buffer, bool fullscreen, bool border,
144
              const std::string&title, int &x, int &y, unsigned int &w, unsigned int &h)
145
  {
146
147
    DWORD dwExStyle;
    DWORD style;
148

149
150
    if (fullscreen) {
      DEVMODE dmScreenSettings;                                                               // Device Mode
151

152
153
154
      if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmScreenSettings)) {
        ::error("GEM: couldn't get screen capabilities!");
      } else {
155
156
        w = dmScreenSettings.dmPelsWidth;
        h = dmScreenSettings.dmPelsHeight;
157
      }
158

159
160
      x=y=0;

161
162
163
164
165
166
167
168
169
170
      memset(&dmScreenSettings,0,
             sizeof(dmScreenSettings));   // Makes Sure Memory's Cleared
      dmScreenSettings.dmSize=sizeof(
                                dmScreenSettings);               // Size Of The Devmode Structure
      dmScreenSettings.dmPelsWidth    =
        w;                    // Selected Screen Width
      dmScreenSettings.dmPelsHeight   =
        h;                    // Selected Screen Height
      dmScreenSettings.dmBitsPerPel   =
        32;                                   // Selected Bits Per Pixel
171
172
      dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
      // Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
173
174
      if (ChangeDisplaySettings(&dmScreenSettings,
                                CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) {
175
176
        dmScreenSettings.dmPelsWidth  = w;
        dmScreenSettings.dmPelsHeight = h;
177
178
        if (ChangeDisplaySettings(&dmScreenSettings,
                                  CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) {
179
180
          ::error("couldn't switch to fullscreen");
          fullscreen=false;
181
182
        }
      }
183
    }
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
    // Since Windows uses some of the window for the border, etc,
    //                we have to ask how big the window should really be
    RECT newSize = getRealRect(x, y, w, h,
                               border, fullscreen,
                               style, dwExStyle);
    // Create the window
    win = CreateWindowEx (
            dwExStyle,
            "GEM",
            title.c_str(),
            style,
            newSize.left,
            newSize.top,
            newSize.right - newSize.left,
            newSize.bottom - newSize.top,
            NULL,
            NULL,
            hInstance,
            NULL);
    if (!win)  {
      throw(GemException("Unable to create window"));
    }
    // create the device context
    dc = GetDC(win);
    if (!dc)  {
      throw(GemException("GEM: Unable to create device context"));
    }
212

213
214
215
216
    // set the pixel format for the window
    if (!bSetupPixelFormat(dc, buffer))  {
      throw(GemException("Unable to set window pixel format"));
    }
217

218
219
220
221
222
    // create the OpenGL context
    context = wglCreateContext(dc);
    if (!context)  {
      throw(GemException("Unable to create OpenGL context"));
    }
223

224
225
226
    // do we share display lists?
    if (sharedContext) {
      wglShareLists(sharedContext, context);
zmoelnig's avatar
zmoelnig committed
227
    }
228

229
230
231
232
233
    // make the context the current rendering context
    if (!wglMakeCurrent(dc, context))   {
      throw(GemException("Unable to make OpenGL context current"));
    }
  }
234

235
236
237
238
239
240
241
242
  void destroy(void)
  {
    if (context) {
      wglDeleteContext(context);
    }
    if (win) {
      if (dc) {
        ReleaseDC(win, dc);
243
244
      }

245
246
      s_winmap.erase(win);
      DestroyWindow(win);
zmoelnig's avatar
zmoelnig committed
247
    }
248

249
250
251
252
    dc  = NULL;
    win = NULL;
    context=NULL;
  }
253

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

  static bool bSetupPixelFormat(HDC hdc, int buffer)
  {
    PIXELFORMATDESCRIPTOR pfd;

    // clean out the descriptor
    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));

    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
    if (buffer == 2) {
      pfd.dwFlags = pfd.dwFlags | PFD_DOUBLEBUFFER;
    }
    pfd.dwLayerMask = PFD_MAIN_PLANE;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cRedBits = 8;
    pfd.cBlueBits = 8;
    pfd.cGreenBits = 8;
    pfd.cDepthBits = 16;
    pfd.cAccumBits = 0;
    pfd.cStencilBits = 8;

    int pixelformat;
    if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0 ) {
      throw(GemException("ChoosePixelFormat failed"));
    }
    if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE) {
      throw(GemException("SetPixelFormat failed"));
      return(false);
285
    }
286
287
    return(true);
  }
288
289
290
291
292

  ////////////////////////////////////////////////////////
  // MainWndProc
  //
  /////////////////////////////////////////////////////////
293
public:
294
295
  static LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam,
                                  LPARAM lParam)
296
297
  {
    gemw32window*obj=s_winmap[hWnd];
298
    if(obj) {
299
      return obj->event(uMsg, wParam, lParam);
300
    }
301

302
303
304
305
306
    return(DefWindowProc (hWnd, uMsg, wParam, lParam));
  }
};

HGLRC gemw32window::Window::sharedContext=NULL;
307
std::map<HWND, gemw32window*>gemw32window::Window::s_winmap;
308

zmoelnig's avatar
zmoelnig committed
309
310
CPPEXTERN_NEW(gemw32window);
gemw32window::gemw32window(void) :
311
  m_topmost(false),
312
  m_win(NULL)
zmoelnig's avatar
zmoelnig committed
313
{
314
  if(!initGemWin()) {
zmoelnig's avatar
zmoelnig committed
315
    throw(GemException("could not initialize window infrastructure"));
316
  }
zmoelnig's avatar
zmoelnig committed
317
}
318
319
320
gemw32window::~gemw32window(void)
{
  destroyMess();
zmoelnig's avatar
zmoelnig committed
321
322
323
324
325
326
327
328
}

/////////////////////////////////////////////////////////
// createGemWindow
//
/////////////////////////////////////////////////////////
bool gemw32window:: create(void)
{
329
330
331
332
  unsigned int w = m_width;
  unsigned int h = m_height;
  int x = m_xoffset;
  int y = m_yoffset;
zmoelnig's avatar
zmoelnig committed
333
334

  static bool firstTime = true;
335

zmoelnig's avatar
zmoelnig committed
336
337
  // Register the frame class
  HINSTANCE hInstance = GetModuleHandle(NULL);
338
339
  if (!hInstance)  {
    error("GEM: Unable to get module instance");
zmoelnig's avatar
zmoelnig committed
340
341
    return false;
  }
342
343
344
  if (firstTime)  {
    WNDCLASS wndclass;
    wndclass.style         = 0;
345
    wndclass.lpfnWndProc   = (WNDPROC)Window::MainWndProc;
346
347
348
349
350
351
352
353
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hCursor       = LoadCursor(NULL, IDC_CROSS);
    wndclass.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
    wndclass.hbrBackground = NULL;
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = "GEM";
354

355
356
357
358
359
    if (!RegisterClass(&wndclass) )  {
      error("GEM: Unable to register window class");
      return false;
    }
    firstTime = false;
zmoelnig's avatar
zmoelnig committed
360
361
  }

362
363
364
  if (NULL==Window::sharedContext) {
    static Window*s_sharedWindow=NULL;
    try {
365
366
367
368
369
370
371
      unsigned int w0=0, h0=0;
      int x0=0, y0=0;
      s_sharedWindow=new Window(NULL, hInstance, 2, false, false, "GEM",
                                x0, y0,
                                w0, h0);
      Window::sharedContext=s_sharedWindow->context;
    } catch (GemException&) {
372
      Window::sharedContext=NULL;
373
    }
374
  }
375
376
  m_win=NULL;
  try {
377
378
    m_win=new Window(this, hInstance, m_buffer, m_fullscreen>0, m_border,
                     m_title,
379
                     x, y, w, h);
380
381
  } catch (GemException&x) {
    error("%s", x.what());
382
    return false;
zmoelnig's avatar
zmoelnig committed
383
  }
384
  // show and update main window
385
  if (m_fullscreen) {
386
387
388
389
    ShowWindow(m_win->win,
               SW_SHOW);                             // Show The Window
    SetForegroundWindow(
      m_win->win);                            // Slightly Higher Priority
390
391
392
    SetFocus(m_win->win);
  } else  {
    ShowWindow(m_win->win, SW_SHOWNORMAL);
393
  }
394

395
396
397
  UpdateWindow(m_win->win);
  dimension(w, h);
  position(x, y);
398
  return createGemWindow();
399
}
400
401
402
403
404
405
406
407
408
409
410
void gemw32window:: createMess(const std::string&s)
{
  if(m_win) {
    error("window already made");
    return;
  }
  if(!create()) {
    destroyMess();
    return;
  }
  topmostMess(m_topmost);
zmoelnig's avatar
zmoelnig committed
411
412
413
414
415
416
417
418
}

/////////////////////////////////////////////////////////
// destroyGemWindow
//
/////////////////////////////////////////////////////////
void gemw32window:: destroy(void)
{
419
420
421
  if (m_fullscreen) {
    ChangeDisplaySettings(NULL,0);  // Switch Back To The Desktop
  }
zmoelnig's avatar
zmoelnig committed
422

423
  if(m_win) {
424
    delete m_win;
425
  }
426
  m_win=NULL;
427

428
  destroyGemWindow();
zmoelnig's avatar
zmoelnig committed
429
430
431
432
433
434
435
436
}

/////////////////////////////////////////////////////////
// switch cursor on/off
//
/////////////////////////////////////////////////////////
void gemw32window::cursorMess(bool state)
{
437
438
439
440
  /* ShowCursor() increments/decrements a counter
   * only if the counter falls below 0, the cursor is hidden...
   */
  while ((ShowCursor(state) < 0) == state) {;}
zmoelnig's avatar
zmoelnig committed
441
442
}

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
void gemw32window::fullscreenMess(int state)
{
  m_fullscreen=state;
  if(!m_win) {
    return;
  }
  unsigned int w = m_width;
  unsigned int h = m_height;
  int x = m_xoffset;
  int y = m_yoffset;

  HINSTANCE hInstance = GetModuleHandle(NULL);
  if (!hInstance)  {
    error("GEM: Unable to get module instance");
    return;
  }
  Window*tmpwin=NULL;
  try {
461
462
    tmpwin=new Window(this, hInstance, m_buffer, m_fullscreen>0, m_border,
                      m_title,
463
464
465
466
                      x, y,
                      w, h);
  } catch (GemException&x) {
    error("unable to toggle fullscreen mode: %s", x.what());
467
468
    post("fullscreen will effect newly created windows.");
    return;
469
470
471
472
473
474
  }
  if(tmpwin) {
    delete m_win;
    m_win=tmpwin;
    // show and update main window
    if (m_fullscreen) {
475
476
477
478
      ShowWindow(m_win->win,
                 SW_SHOW);                             // Show The Window
      SetForegroundWindow(
        m_win->win);                            // Slightly Higher Priority
479
480
481
      SetFocus(m_win->win);
    } else  {
      ShowWindow(m_win->win, SW_SHOWNORMAL);
482
    }
483
484
485
486
    UpdateWindow(m_win->win);
    dimension(w, h);
    position(x, y);
  }
487
488
}

zmoelnig's avatar
zmoelnig committed
489
490
491
492
493
494
/////////////////////////////////////////////////////////
// set topmost position on/off
//
/////////////////////////////////////////////////////////
void gemw32window::topmostMess(bool state)
{
495
496
  m_topmost=state;

497
  if(m_win) {
498
499
    SetWindowPos(m_win->win, (state?HWND_TOPMOST:HWND_NOTOPMOST), 0, 0, 0, 0,
                 SWP_NOSIZE | SWP_NOMOVE);
500
  }
zmoelnig's avatar
zmoelnig committed
501
}
502
503
504
505
/////////////////////////////////////////////////////////
// dimensionsMess
//
/////////////////////////////////////////////////////////
506
507
void gemw32window :: dimensionsMess(unsigned int width,
                                    unsigned int height)
508
{
509
  if (width < 1) {
510
511
512
    error("width must be greater than 0");
    return;
  }
513

514
  if (height < 1) {
515
516
517
518
519
520
    error ("height must be greater than 0");
    return;
  }

  m_width=width;
  m_height=height;
521
522

  move();
523
}
524
525
526
527
void gemw32window :: offsetMess(int x, int y)
{
  GemWindow::offsetMess(x, y);
  move();
528
529
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
void gemw32window::move(void)
{
  if(m_win) {
    DWORD style, exStyle;
    RECT newSize = Window::getRealRect(m_xoffset, m_yoffset, m_width, m_height,
                                       m_border, m_fullscreen>0,
                                       style, exStyle);

    //MoveWindow(m_win->win, m_xoffset, m_yoffset, m_width, m_height, true);
    MoveWindow(m_win->win, newSize.left,
               newSize.top,
               newSize.right - newSize.left,
               newSize.bottom - newSize.top,
               true);
544

545
  }
546
547
}

548
549
550
551
552
553
void gemw32window:: titleMess(const std::string&s)
{
  m_title=s;
  if(m_win) {
    SetWindowText(m_win->win, s.c_str());
  }
554
}
zmoelnig's avatar
zmoelnig committed
555
556
557

void gemw32window::swapBuffers(void)
{
558
  SwapBuffers(m_win->dc);
zmoelnig's avatar
zmoelnig committed
559
560
561
562
}

bool gemw32window::makeCurrent(void)
{
563
564
565
  if (!m_win) {
    return false;
  }
566
  wglMakeCurrent(m_win->dc, m_win->context);
zmoelnig's avatar
zmoelnig committed
567
568
569
570
571
572
  return true;
}

void gemw32window::dispatch(void)
{
  MSG msg;
573
574
575
576
  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
zmoelnig's avatar
zmoelnig committed
577
}
578
579
LONG WINAPI gemw32window::event(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
580

581
582
  static RECT rcClient;
  static int ctrlKeyDown = 0;
583

584
  // assume that we handle the message
585
  long lRet = 0;
586
  int devID=0;
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  switch (uMsg) {
  // mouse motion
  case WM_MOUSEMOVE:
    motion(devID, LOWORD(lParam), HIWORD(lParam));
    break;

  // left button up/down
  case WM_LBUTTONUP:
  case WM_LBUTTONDOWN:
    button(devID, 0, (uMsg==WM_LBUTTONDOWN));
    motion(devID, LOWORD(lParam), HIWORD(lParam));
    break;
  // middle button up/down
  case WM_MBUTTONUP:
  case WM_MBUTTONDOWN:
    button(devID, 1, (uMsg==WM_MBUTTONDOWN));
    motion(devID, LOWORD(lParam), HIWORD(lParam));
    break;
  // middle button up/down
  case WM_RBUTTONUP:
  case WM_RBUTTONDOWN:
    button(devID, 2, (uMsg==WM_RBUTTONDOWN));
    motion(devID, LOWORD(lParam), HIWORD(lParam));
    break;

  // keyboard action
  case WM_KEYUP:
  case WM_KEYDOWN:
    key(devID, (char*)&wParam, (int)wParam, (uMsg==WM_KEYDOWN));
    break;
  // resize event
  case WM_SIZE:
    m_width=LOWORD(lParam);
    m_height=HIWORD(lParam);
    dimension(m_width, m_height);
    //GetClientRect(m_win->win, &rcClient);
    break;
  case WM_MOVE:
    m_xoffset=LOWORD(lParam);
    m_yoffset=HIWORD(lParam);
    position(m_xoffset, m_yoffset);
    break;

  // we want to override these messages
  // and not do anything (rather let the user react and programmatically destroy)
  case WM_DESTROY:
  case WM_CLOSE:
    do {
      std::vector<t_atom>al;
      t_atom a;
      SETSYMBOL(&a, gensym("window"));
      al.push_back(a);
      SETSYMBOL(&a, gensym("destroy"));
      al.push_back(a);
      info(al);
    } while(0);
    break;
  case WM_CREATE: {
  }
  break;
647

648
649
650
651
652
  // pass all unhandled messages to DefWindowProc
  default:
    lRet = DefWindowProc (m_win->win, uMsg, wParam, lParam);
    break;
  }
653
654
  return(lRet);
}
zmoelnig's avatar
zmoelnig committed
655
656
657
658
659
660
661
662
663



/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////
void gemw32window :: obj_setupCallback(t_class *classPtr)
{
664
  CPPEXTERN_MSG1(classPtr, "topmost", topmostMess, bool);
zmoelnig's avatar
zmoelnig committed
665
666
}
#endif /* WIN32 */