//---------------------------------------------------------------------------- int WindowApplication::Main (int numArguments, char** arguments) { // Initialize the extra data. TODO: Port the extra-data system of WM4 // to WM5. memset(gsExtraData, 0, APP_EXTRA_DATA_QUANTITY*sizeof(char)); WindowApplication* theApp = (WindowApplication*)TheApplication; theApp->KEY_TERMINATE = KEY_ESCAPE; // OpenGL uses a projection matrix for depth in [-1,1]. Camera::SetDefaultDepthType(Camera::PM_DEPTH_MINUS_ONE_TO_ONE); // Allow work to be done before the window is created. if (!theApp->OnPrecreate()) { return -1; } // Connect to the X server. const char* displayName = 0; Display* display = XOpenDisplay(displayName); if (!display) { return -2; } // Make sure the X server has OpenGL GLX extensions. int errorBase, eventBase; Bool success = glXQueryExtension(display, &errorBase, &eventBase); assertion(success == True, "GLX extensions not found.\n"); if (!success) { return -3; } // Partial construction of a GLX renderer. The window for the renderer // is not yet constructed. When it is, the window identifier is supplied // to the renderer to complete its construction. RendererInput input; input.mDisplay = display; input.mVisual = 0; input.mContext = 0; mRenderer = new0 Renderer(input, theApp->GetWidth(), theApp->GetHeight(), mColorFormat, mDepthStencilFormat, mNumMultisamples); if (!input.mVisual || !input.mContext) { return -4; } // Create an X Window with the visual information created by the renderer // constructor. The visual information might not be the default, so // create an X colormap to use. Window rootWindow = RootWindow(display, input.mVisual->screen); Colormap cMap = XCreateColormap(display, rootWindow, input.mVisual->visual, AllocNone); // Set the event mask to include exposure (paint), button presses (mouse), // and key presses (keyboard). XSetWindowAttributes windowAttributes; windowAttributes.colormap = cMap; windowAttributes.border_pixel = 0; windowAttributes.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask; int xPos = theApp->GetXPosition(); int yPos = theApp->GetYPosition(); unsigned int width = (unsigned int)theApp->GetWidth(); unsigned int height = (unsigned int)theApp->GetHeight(); unsigned int borderWidth = 0; unsigned long valueMask = CWBorderPixel | CWColormap | CWEventMask; Window window = XCreateWindow(display, rootWindow, xPos, yPos, width, height, borderWidth, input.mVisual->depth, InputOutput, input.mVisual->visual, valueMask, &windowAttributes); XSizeHints hints; hints.flags = PPosition | PSize; hints.x = xPos; hints.y = yPos; hints.width = width; hints.height = height; XSetNormalHints(display, window, &hints); const char* iconName = theApp->GetWindowTitle(); Pixmap iconPixmap = None; XSetStandardProperties(display, window, theApp->GetWindowTitle(), iconName, iconPixmap, arguments, numArguments, &hints); // Intercept the close-window event when the user selects the // window close button. The event is a "client message". Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &wmDelete, 1); // Finish the construction of the renderer. GlxRendererData* data = (GlxRendererData*)mRenderer->mData; if (!data->FinishConstruction(window, mRenderer)) { return -5; } if (theApp->OnInitialize()) { // The default OnPreidle() clears the buffers. Allow the application // to fill them before the window is shown and before the event loop // starts. theApp->OnPreidle(); // Display the window. XMapWindow(display, window); // Start the message pump. bool applicationRunning = true; while (applicationRunning) { if (!XPending(display)) { theApp->OnIdle(); continue; } XEvent evt; XNextEvent(display, &evt); int index; bool state; if (evt.type == ButtonPress || evt.type == ButtonRelease) { theApp->OnMouseClick(evt.xbutton.button, evt.xbutton.type, evt.xbutton.x, evt.xbutton.y, evt.xbutton.state); if (evt.type == ButtonPress) { index = GLXAPP_BUTTONDOWN + evt.xbutton.button; state = true; SetExtraData(index, sizeof(bool), &state); } else { index = GLXAPP_BUTTONDOWN + evt.xbutton.button; state = false; SetExtraData(index, sizeof(bool), &state); } continue; } if (evt.type == MotionNotify) { int button = 0; index = GLXAPP_BUTTONDOWN + 1; GetExtraData(index, sizeof(bool), &state); if (state) { button = MOUSE_LEFT_BUTTON; } else { index = GLXAPP_BUTTONDOWN + 2; GetExtraData(index, sizeof(bool), &state); if (state) { button = MOUSE_MIDDLE_BUTTON; } else { index = GLXAPP_BUTTONDOWN + 3; GetExtraData(index, sizeof(bool), &state); if (state) { button = MOUSE_RIGHT_BUTTON; } } } if (button > 0) { theApp->OnMotion(button, evt.xmotion.x, evt.xmotion.y, evt.xmotion.state); } else { theApp->OnPassiveMotion(evt.xmotion.x, evt.xmotion.y); } continue; } if (evt.type == KeyPress || evt.type == KeyRelease) { KeySym keySym = XKeycodeToKeysym(display, evt.xkey.keycode, 0); int key = (keySym & 0x00FF); // Quit application if the KEY_TERMINATE key is pressed. if (key == theApp->KEY_TERMINATE) { XDestroyWindow(display, window); applicationRunning = false; continue; } // Adjust for special keys that exist on the key pad and on // the number pad. if ((keySym & 0xFF00) != 0) { if (0x50 <= key && key <= 0x57) { // keypad Home, {L,U,R,D}Arrow, Pg{Up,Dn}, End key += 0x45; } else if (key == 0x63) { // keypad Insert key = 0x9e; } else if (key == 0xFF) { // keypad Delete key = 0x9f; } else if (key == 0xE1 || key == 0xE2) { // L-shift or R-shift key = KEY_SHIFT; state = (evt.type == KeyPress); SetExtraData(GLXAPP_SHIFTDOWN, sizeof(bool), &state); } else if (key == 0xE3 || key == 0xE4) { // L-ctrl or R-ctrl key = KEY_CONTROL; } else if (key == 0xE9 || key == 0xEA) { // L-alt or R-alt key = KEY_ALT; } else if (key == 0xEB || key == 0xEC) { key = KEY_COMMAND; } } if ((KEY_HOME <= key && key <= KEY_END) || (KEY_F1 <= key && key <= KEY_F12) || (KEY_SHIFT <= key && key <= KEY_COMMAND)) { if (evt.type == KeyPress) { theApp->OnSpecialKeyDown(key, evt.xbutton.x, evt.xbutton.y); } else { theApp->OnSpecialKeyUp(key, evt.xbutton.x, evt.xbutton.y); } } else { // Get key-modifier state. Adjust for shift state. unsigned char ucKey = (unsigned char)key; GetExtraData(GLXAPP_SHIFTDOWN, sizeof(bool), &state); if (state && 'a' <= ucKey && ucKey <= 'z') { ucKey = (unsigned char)(key - 32); } if (evt.type == KeyPress) { theApp->OnKeyDown(ucKey, evt.xbutton.x, evt.xbutton.y); } else { theApp->OnKeyUp(ucKey, evt.xbutton.x, evt.xbutton.y); } } continue; } if (evt.type == Expose) { theApp->OnDisplay(); continue; } if (evt.type == ConfigureNotify) { theApp->OnMove(evt.xconfigure.x, evt.xconfigure.y); theApp->OnResize(evt.xconfigure.width, evt.xconfigure.height); continue; } if (evt.type == ClientMessage && evt.xclient.data.l[0] == wmDelete) { XDestroyWindow(display, window); applicationRunning = false; continue; } } } theApp->OnTerminate(); XCloseDisplay(display); return 0; }
//---------------------------------------------------------------------------- int WindowApplication::Main (int, char**) { WindowApplication* theApp = (WindowApplication*)TheApplication; theApp->KEY_TERMINATE = WindowApplication::KEY_ESCAPE; #ifdef WM5_USE_DX9 // DirectX uses a projection matrix for depth in [0,1]. Camera::SetDefaultDepthType(Camera::PM_DEPTH_ZERO_TO_ONE); #endif #ifdef WM5_USE_OPENGL // OpenGL uses a projection matrix for depth in [-1,1]. Camera::SetDefaultDepthType(Camera::PM_DEPTH_MINUS_ONE_TO_ONE); #endif // Allow work to be done before the window is created. if (!theApp->OnPrecreate()) { return -1; } // === Create the window for rendering. === // Register the window class. static char sWindowClass[] = "Wild Magic 5 Application"; WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = MsWindowEventHandler; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = 0; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = sWindowClass; wc.lpszMenuName = 0; RegisterClass(&wc); DWORD dwStyle; if (mAllowResize) { dwStyle = WS_OVERLAPPEDWINDOW; } else { // This removes WS_THICKFRAME and WS_MAXIMIZEBOX, both of which allow // resizing of windows. dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; } // Require the window to have the specified client area. RECT rect = { 0, 0, theApp->GetWidth()-1, theApp->GetHeight()-1 }; AdjustWindowRect(&rect, dwStyle, FALSE); // Create the application window. HWND handle = CreateWindow(sWindowClass, theApp->GetWindowTitle(), dwStyle, theApp->GetXPosition(), theApp->GetYPosition(), rect.right - rect.left + 1, rect.bottom - rect.top + 1, 0, 0, 0, 0); // Save the handle as an 'int' for portable handle storage. theApp->SetWindowID(PtrToInt(handle)); // === #ifdef WM5_USE_DX9 // Create the device for rendering. RendererInput input; input.mWindowHandle = handle; input.mDriver = Direct3DCreate9(D3D_SDK_VERSION); assertion(input.mDriver != 0, "Failed to create Direct3D9\n"); mRenderer = new0 Renderer(input, theApp->GetWidth(), theApp->GetHeight(), mColorFormat, mDepthStencilFormat, mNumMultisamples); #endif #ifdef WM5_USE_OPENGL // The pixelFormat variable is used to support creation of a window that // supports multisampling. This process requires creating a normal window, // and then querying whether the renderer supports multisampling. The // renderer creates a device context for the window which we then need to // create a second window that supports multisampling. The device context // rendererDC is set by the renderer during the process. RendererInput input; input.mWindowHandle = handle; input.mPixelFormat = 0; input.mRendererDC = 0; input.mDisableVerticalSync = true; mRenderer = new0 Renderer(input, theApp->GetWidth(), theApp->GetHeight(), mColorFormat, mDepthStencilFormat, mNumMultisamples); // To determine whether multisampling is supported, it is necessary to // create a window, an OpenGL context, and query the driver for the // multisampling extensions. If it is, a new window must be created // because the renderer creation involves SetPixelFormat(...) that can // be called only once for a window. int numMultisamples = mRenderer->GetNumMultisamples(); if (numMultisamples > 0) { int attributes[256], pos = 0; attributes[pos++] = WGL_SUPPORT_OPENGL_ARB; attributes[pos++] = 1; attributes[pos++] = WGL_DRAW_TO_WINDOW_ARB; attributes[pos++] = 1; attributes[pos++] = WGL_ACCELERATION_ARB; attributes[pos++] = WGL_FULL_ACCELERATION_ARB; attributes[pos++] = WGL_PIXEL_TYPE_ARB; attributes[pos++] = WGL_TYPE_RGBA_ARB; attributes[pos++] = WGL_RED_BITS_ARB; attributes[pos++] = 8; attributes[pos++] = WGL_GREEN_BITS_ARB; attributes[pos++] = 8; attributes[pos++] = WGL_BLUE_BITS_ARB; attributes[pos++] = 8; attributes[pos++] = WGL_ALPHA_BITS_ARB; attributes[pos++] = 8; attributes[pos++] = WGL_DEPTH_BITS_ARB; attributes[pos++] = 24; attributes[pos++] = WGL_STENCIL_BITS_ARB; attributes[pos++] = 8; attributes[pos++] = WGL_DOUBLE_BUFFER_ARB; attributes[pos++] = 1; attributes[pos++] = WGL_SAMPLE_BUFFERS_ARB; attributes[pos++] = 1; attributes[pos++] = WGL_SAMPLES_ARB; attributes[pos++] = numMultisamples; attributes[pos++] = 0; // list is zero-terminated unsigned int numFormats = 0; BOOL successful = wglChoosePixelFormatARB(input.mRendererDC, attributes, 0, 1, &input.mPixelFormat, &numFormats); if (successful && numFormats > 0) { // The card supports multisampling with the requested number of // samples. Recreate the window and renderer. delete0(mRenderer); gsIgnoreWindowDestroy = true; DestroyWindow(handle); handle = CreateWindow(sWindowClass, theApp->GetWindowTitle(), WS_OVERLAPPEDWINDOW, theApp->GetXPosition(), theApp->GetYPosition(), rect.right - rect.left + 1, rect.bottom - rect.top + 1, 0, 0, 0, 0); theApp->SetWindowID(PtrToInt(handle)); input.mWindowHandle = handle; mRenderer = new0 Renderer(input, theApp->GetWidth(), theApp->GetHeight(), mColorFormat, mDepthStencilFormat, mNumMultisamples); } } #endif if (theApp->OnInitialize()) { // The default OnPreidle() clears the buffers. Allow the application // to fill them before the window is shown and before the event loop // starts. theApp->OnPreidle(); // Display the window. ShowWindow(handle, SW_SHOW); UpdateWindow(handle); // Start the message pump. bool applicationRunning = true; while (applicationRunning) { MSG msg; if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { applicationRunning = false; continue; } HACCEL accel = 0; if (!TranslateAccelerator(handle, accel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } else { theApp->OnIdle(); } } } theApp->OnTerminate(); delete0(mRenderer); #ifdef WM5_USE_DX9 input.mDriver->Release(); #endif return 0; }