Windows 在WinAPI中,如何使用正确的鼠标制作一个可伸缩的OpenGL窗口,使其适合游戏?

Windows 在WinAPI中,如何使用正确的鼠标制作一个可伸缩的OpenGL窗口,使其适合游戏?,windows,winapi,opengl,resizable,createwindowex,Windows,Winapi,Opengl,Resizable,Createwindowex,多年来,我在努力理解Microsoft Windows的CreateWindowEx()这个奇怪的世界时,读到了很多关于堆栈溢出的文章。。当最初被问到“在WinAPI中创建可流动调整大小的OpenGL窗口的最佳方法是什么?” 我一直在努力让WinAPI制作一个窗口: 具有OpenGL上下文 在“窗口”模式或“全屏”模式下,在多监视器和单监视器显示中,正确居中于主监视器(或由命令行信号确定的任何监视器)上 具有固定的内部客户端屏幕大小(视口2d) 不允许您在错误的时间或在多显示器的特殊情况下单击

多年来,我在努力理解Microsoft Windows的
CreateWindowEx()
这个奇怪的世界时,读到了很多关于堆栈溢出的文章。。当最初被问到“在WinAPI中创建可流动调整大小的OpenGL窗口的最佳方法是什么?”

我一直在努力让WinAPI制作一个窗口:

  • 具有OpenGL上下文
  • 在“窗口”模式或“全屏”模式下,在多监视器和单监视器显示中,正确居中于主监视器(或由命令行信号确定的任何监视器)上
  • 具有固定的内部客户端屏幕大小(视口2d)
  • 不允许您在错误的时间或在多显示器的特殊情况下单击外部,导致其失去焦点
  • 可以流畅地调整大小,但不会更改内部“客户端大小”(这意味着它将固定大小的OpenGL内容拉伸为新的屏幕大小)。。。这里的想法是添加一层虚拟化,以便所有像素都以相同的1920x1080(1080p)坐标系表示。这部分对我来说没问题
  • 通过屏幕->客户端比率正确处理屏幕大小->客户端大小等效项的鼠标事件转换
在我自己开发的应用程序框架中,我必须设置显示大小,即使如此,Windows也没有给我正确大小的窗口。(例如,有时减去标题栏,有时减去滚动条,但在标题栏下绘制上下文。)

此外,最近从2010 EE(Win32/Windows 7)移动到2015(Win32/Windows 10)时,我不得不更改参数以重新居中视图,因为它在主显示器上偏离中心。现在,只有有时这些值是正确的或不正确的。例如,如果我选择“全屏”,屏幕顶部将绘制相同的值,这样屏幕底部就会有一个区域显示“gl clear color”(在我的例子中为橙色)

我可以通过提供以下命令行参数来处理这些问题:

  • -带边框(默认,实际上没有效果,是带有标题栏等的默认窗口模式)
  • -无边界(似乎进入全屏模式,应用程序偏离中心,win 0,0实际上位于屏幕中心)
  • -窗口化(或-窗口化)
如果我不提供-window,它默认为“全屏”分辨率调整(但只有在我假设支持的情况下,否则可能会抛出错误)

总之,这一切都很糟糕,因为 a) 我必须为我正在使用的每种分辨率写上百万个屏幕,而不是写下1080p的所有内容,并让它调整到显示大小,这正是我想要的,因为它可以处理笔记本电脑和台式机上的大多数新显示(记住,这是Windows)(在那些角落的屏幕上只会稍微挤压一些东西) b) 我无法流畅地调整窗口大小,而且我必须将鼠标放在中心并重新计算鼠标位置,因此我只记录delta——这是为了避免鼠标离开窗口并单击桌面,或从显示器浮动到其他显示器,即使它是隐藏的。我还必须使鼠标光标不可见,这样用户就看不到这一点,然后显示一个模拟的鼠标光标。 c) 不支持1920x1080的用户将无法使用全屏模式

有人在另一个问题()中指出了这篇文章:

我通读了这篇文章,了解到AdjustWindowRectEx()有一些问题:

我不使用WS_重叠,因此这只是有一定帮助:

我现在是这样做的:

    display.Resized(display.w,display.h);

    // Fill in the window class structure for testing display type.

    winclass.cbSize = sizeof(WNDCLASSEX);
    winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    winclass.lpfnWndProc = WinProc;
    winclass.cbClsExtra = 0;
    winclass.cbWndExtra = 0;
    winclass.hInstance = hinstance;
    winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winclass.lpszMenuName = NULL;
    winclass.lpszClassName = WINDOW_CLASS_NAME;

    // Save the game instance handle
    display.hinstance = game_instance = hinstance;

    // Register the window class
    if (!RegisterClassEx(&winclass)) return(0);

    if (!gl.Init(hinstance, display.bits)) {
     return(0);
    }

    // Detect the display size and create the final display profile 

    DWORD winStyle=
     WS_EX_APPWINDOW |
     WS_EX_TOPMOST /*|
     WS_EX_ACCEPTFILES*/ ;

    // Adjust Window, Account For Window Borders
    int xPos = GetSystemMetrics(SM_CXSCREEN) - display.w;
    int yPos = GetSystemMetrics(SM_CYSCREEN) - display.h;
    RECT windowRect = {0, 0, display.w, display.h}; // Define Our Window Coordinates
    AdjustWindowRectEx (&windowRect, WS_POPUP, 0, winStyle );
    // Create the window
    if (!(hwnd = CreateWindowEx(
     winStyle,                    // extended style
     WINDOW_CLASS_NAME, // class
     gl.winTitle.c_str(),          // title
     ( gl.borderless || CmdLine.Option("-borderless") ) ? (WS_POPUPWINDOW | WS_VISIBLE)
     : (gl.noFullscreen ? ((CmdLine.Option("-bordered") ? WS_BORDER : 0) | WS_VISIBLE)
                        : (WS_POPUP | WS_VISIBLE)),  // use POPUP for full screen
       gl.noFullscreen && !CmdLine.Option("-recenter") ? xPos / 2 : 0,
       gl.noFullscreen && !CmdLine.Option("-recenter") ? yPos / 2 : 0,     // initial game window x,y
       display.w,         // initial game width
       display.h,         // initial game height
       HWND_DESKTOP,      // handle to parent
       NULL,              // handle to menu
       hinstance,         // instance of this application
       NULL)
      )         // extra creation parms
     ) {
     OUTPUT("WinAPI ERROR: Could not open window.\n");
     return(0);
    }

    if (gl.borderless || CmdLine.Option("-borderless") ) {
      LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
      lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
      SetWindowLong(hwnd, GWL_STYLE, lStyle);
      LONG lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
      lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
      SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle);
      SetWindowPos(hwnd, NULL, 0, 0, display.w, display.h, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
      }

      // Temporary change to full screen mode
      ZeroMemory(&game_screen, sizeof(game_screen)); // clear out size of DEVMODE    struct
    game_screen.dmSize = sizeof(game_screen);
    game_screen.dmPelsWidth = display.w;
    game_screen.dmPelsHeight = display.h;
    game_screen.dmBitsPerPel = display.bits;
    game_screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    ChangeDisplaySettings(&game_screen, CDS_FULLSCREEN);

    // save the game window handle
    display.hwnd = game_window = hwnd;
    display.hdc = game_dc = GetDC(display.hwnd = game_window); // get the GDI device context
                                                             // set up the pixel format desc struct

    pfd = {
     sizeof(PIXELFORMATDESCRIPTOR),   // size of this PFD
     1,                               // version number
     PFD_DRAW_TO_WINDOW |             // supports window
     PFD_SUPPORT_OPENGL |             // supports OpenGL
     PFD_DOUBLEBUFFER,                // support double buff
     PFD_TYPE_RGBA,                   // request RGBA format
     (BYTE)display.bits,              // select color depth
     0, 0, 0, 0, 0, 0,                // color bits ignored
     0,                               // no alpha buff
     0,                               // shift bit ignored
     0,                               // no accum buff
     0, 0, 0, 0,                      // accum bits ignored
     16,                              // 16-bit Z-buff (depth buff)
     0,                               // no stencil buff
     0,                               // no aux buff
     PFD_MAIN_PLANE,                  // main drawing layer
     0,                               // reserved
     0, 0, 0                          // layer masks ignored
    };
    int pf;  // pixel format

    if (!gl.arbMultisampleSupported) {
     if (!(pf = ChoosePixelFormat(game_dc, &pfd)))  // match the pixel format
     {
      MessageBox(game_window, "OpenGL could not be initialized -- ChoosePixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
      return FALSE; // error returned
     }
    } else {
     pf = gl.arbMultisampleFormat;
    }
    if (!SetPixelFormat(game_dc, pf, &pfd))        // set the pixel format
    {
     MessageBox(game_window, "OpenGL could not be initialized -- SetPixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
     return FALSE; // error returned
    }

    if (!(game_rc = wglCreateContext(game_dc)))    // create the rendering context
    {
     MessageBox(game_window, "OpenGL could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
     return FALSE; // error returned
    }

    if (!(upload_rc = wglCreateContext(game_dc)))    // create the rendering context
    {
     MessageBox(game_window, "Multiple OpenGL contexts could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
     return FALSE; // error returned
    } else { // Share as much as you can between two contexts
     if (!wglShareLists(game_rc, upload_rc)) {
      // could use GetLastError here
      MessageBox(game_window, "wglShareLists -- Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
      return FALSE; // error returned
     }
    }

    if (!wglMakeCurrent(game_dc, display.hglrc = game_rc))         // make it  current
    {
     MessageBox(game_window, "OpenGL could not be initialized -- MakeCurrent Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
     return FALSE; // error returned
    }

    ShowCursor(false);
    ShowWindow(game_window, SW_SHOWNORMAL);
    SetForegroundWindow(game_window);
在上面的代码中,我得到的是一个没有调整大小功能的窗口,它隐藏操作系统鼠标光标,并且只能使用ALT-TAB(或ALT-F4)退出,退出时它会显示在windows Z顺序的后面。我总是使用一个参数打开我的窗口,该参数将display.w设置为1920,display.h设置为1080,可以是全屏模式,也可以是窗口模式。然后调用WM_SIZE将其调整到客户端区域

请注意,以下WM_大小在我设置display.Resized(w,h)的初始时间之后的WinProc期间调用:

这在应用程序加载期间只执行一次,在第一种情况下,它的值看起来是:19181078


更新:如果我在这里使用GetWindowRect()的结果,或者如下图所示使用GetClientRect()的结果,窗口会神秘地移动到屏幕的中心X,中心Y!什么给

    //  RECT rect;
    //  if ( GetClientRect(hwnd,&rect) ) {
    //   display.Resized((int)rect.right,(int)rect.bottom);
    //  }
      //if ( GetWindowRect( hwnd, &rect ) ) {
      // display.Resized((int)ADIFF(rect.left,rect.right),(int)ADIFF(rect.top,rect.bottom));
      //}
      display.Resized(LOWORD(lparam), HIWORD(lparam));
      return (0);

我需要采取哪些步骤使窗口可伸缩,以便根据视图调整上下文大小,并根据屏幕比例适当调整鼠标

基本上,有太多的边缘案例来解释所有这些。自从两年前我问这个问题以来,随着时间的推移,我发现在全屏和视窗显示之间还有其他不一致之处

据我所知,基本上有3种类型的窗口:

  • 用于窗口化GUI的常规屏幕上可移动/可调整大小的窗口,如此浏览器窗口(如果您不在移动设备上)
  • 一个与显示器的分辨率支持相匹配(包括比其本机分辨率小的分辨率)——我们称之为“全屏”(或全屏,甚至不是一个单词)
  • 一个普通的屏幕窗口,但缺少标题栏、边框和滚动条,显示与屏幕一样大。将“街上”称为“无边界窗口”
我想掌握所有这些,但在某种程度上,使他们都可以访问,而不需要特殊情况。我基本上已经放弃了使用WinAPI,但显然有多家公司都这么做了。遵循Microsoft的文档并没有多大帮助,我已经尝试了许多不同的CreateWindow CreateWindowEx——顺便说一句,这些功能中的许多都不受欢迎,或者根本不起作用

(也许最好的问题是,微软什么时候会退出
    //  RECT rect;
    //  if ( GetClientRect(hwnd,&rect) ) {
    //   display.Resized((int)rect.right,(int)rect.bottom);
    //  }
      //if ( GetWindowRect( hwnd, &rect ) ) {
      // display.Resized((int)ADIFF(rect.left,rect.right),(int)ADIFF(rect.top,rect.bottom));
      //}
      display.Resized(LOWORD(lparam), HIWORD(lparam));
      return (0);