Ubuntu(SDL + OpenGL)中的全屏模式

Fullscreen modes in Ubuntu (SDL + OpenGL)

我在Ubuntu上的全屏行为有些奇怪的问题。窗口模式可以正常工作,但"假"(SDL_WINDOW_FULLSCREEN)和"真实"(SDL_WINDOW_FULLSCREEN_DESKTOP)全屏模式均不能。

在Windows上,"假"全屏模式会改变视频模式,并且我们看到窗口的内容延伸到整个显示器。"真实"全屏模式占用桌面的大小。在这种模式下,我在左上角绘制窗口的内容,并将多余的区域留空。

在Ubuntu上,"假"全屏模式会更改视频模式,并且窗口的内容会拉伸到整个显示器,但是只绘制了一部分。它要么是顶部(占显示的90%以上),要么是底部(占显示的10%以下)。未绘制的部分是黑色的,或者包含在应用程序被声明之前在屏幕上绘制的内容。有时应用程序不会在退出时更改视频模式。即使只绘制底部,光标也被锁定在顶部内部。

"真实"全屏模式要么全黑,要么在显示屏中央显示窗口内容。内容移至顶部,该区域周围的所有内容均为黑色(背景颜色并非如此)。在此模式下,光标锁定在该区域内。

如果我在运行时更改全屏模式,则行为会有所不同-绘制区域(也是锁定光标的区域)可以在任何地方,不仅在"假"模式下位于顶部/底部,在"真实"位置居中。

  • Windows上的"假"全屏模式:><br />
(800x600影片模式)
</li>
<li>
Windows上的
    (800x600窗口的内容绘制在角落,其他区域为空)
  • Ubuntu上的"假"全屏模式:fake ubuntu
    (800x600视频模式,但看不到整个区域。不过,您可以看到gedit的一部分。)
  • Ubuntu上的"真实"全屏模式:real ubuntu
    (位于中心的800x600区域,但图像移到顶部(红色正方形表示在角落)

我手动使用OpenGL而不是SDL在屏幕上绘制。我写了一个小例子来说明问题。它会创建一个800x600的窗口,并在其中心绘制一个蓝色三角形。在屏幕的一角绘制绿色正方形,在窗口的一角绘制红色正方形(这些正方形可以在同一位置,因此绿色正方形更大,在其顶部绘制红色正方形)。可以分别使用" 1"," 2"或" 3"键进入窗口化,"伪造"或"真实"全屏模式。退出键关闭应用程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include <SDL.h>
#include <GL/gl.h>

#define FULLSCREEN 0
#define WIDTH 800
#define HEIGHT 600

//fullscreen:
// 0 - windowed mode,
// 1 -"fake" fullscreen mode,
// 2 -"real" fullscreen mode

int real_width;
int real_height;

//screen width & height - to draw green square that shows us actual screen size

void set_viewport(SDL_Window* window);
void draw(SDL_Window* window);

int main(int argc, char* argv[]) {
 SDL_Init(SDL_INIT_VIDEO);

 int position;
 #if FULLSCREEN == 0
  position = SDL_WINDOWPOS_CENTERED;
 #else
  position = 0;
 #endif

 //the only thing I've found - some guy said it works
 //if window is in (0,0) while entering fullscreen
 //well, it's not, but I've kept it

 int flags = SDL_WINDOW_OPENGL;
 #if FULLSCREEN == 1
  flags |= SDL_WINDOW_FULLSCREEN;
 #elif FULLSCREEN == 2
  flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 #endif

 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

 SDL_Window* window = SDL_CreateWindow(
 "",
  position, position, /* centered/(0,0) */
  WIDTH, HEIGHT, /* request 800x600 window */
  flags /* needed mode */
 );

 //setup GL
 SDL_GLContext glcontext = SDL_GL_CreateContext(window);

 glShadeModel(GL_SMOOTH);
 glCullFace(GL_BACK);
 glFrontFace(GL_CCW);
 glEnable(GL_CULL_FACE);

 //viewport and projection
 set_viewport(window);

 draw(window);

 bool done = false;
 while(!done) {
  draw(window);

  SDL_Event event;
  while(SDL_PollEvent(&event)) {
   switch(event.type) {
    case SDL_QUIT: done = true; break;
    case SDL_KEYDOWN:
     if(event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
      done = true;
     else if(event.key.keysym.scancode == SDL_SCANCODE_1) {
      SDL_SetWindowFullscreen(window, 0);
      set_viewport(window);
     } else if(event.key.keysym.scancode == SDL_SCANCODE_2) {
      SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
      set_viewport(window);
     } else if(event.key.keysym.scancode == SDL_SCANCODE_3) {
      SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
      set_viewport(window);
     }
    break;
   }
  }
 }

 SDL_GL_DeleteContext(glcontext);
 SDL_DestroyWindow(window);
 SDL_Quit();
 return 0;
}

void set_viewport(SDL_Window* window) {
 SDL_GetWindowSize(window, &real_width, &real_height);

 glClearColor(1, 1, 1, 1);
 glViewport(0, 0, real_width, real_height);
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 glOrtho(0, real_width, real_height, 0, -1, 0);
}

void draw_triangle();
void draw_square(int x, int y, float side);

void draw(SDL_Window* window) {
 glClear(GL_COLOR_BUFFER_BIT);
 glEnable(GL_BLEND);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

 //triangle on while background
 glClearColor(1, 1, 1, 1);
 draw_triangle();

 //green square at screen corner
 glColor3f(0, 1, 0);
 draw_square(real_width, real_height, 20);

 //red square at window corner
 glColor3f(1, 0, 0);
 draw_square(WIDTH, HEIGHT, 10);

 SDL_GL_SwapWindow(window);
}

void draw_triangle() {
 const float w = 460;
 const float h = 400;

 float colorBuffer[9] = {
  0, 0.43f, 0.85f /*#006dd9*/,
  0, 0.22f, 0.43f /*#00376e*/,
  0, 0.43f, 0.85f /*#006dd9*/
 };
 float vertexBuffer[9] = {
  0, 0, 0,
  w/2, h, 0,
  w, 0, 0
 };

 glEnableClientState(GL_VERTEX_ARRAY);
 glEnableClientState(GL_COLOR_ARRAY);

 glTranslatef((WIDTH-w)/2,(HEIGHT-h)/2,0);

 glColorPointer(3, GL_FLOAT, 0, colorBuffer);
 glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
 glDrawArrays(GL_TRIANGLE_FAN, 0, 3);

 glTranslatef(-(WIDTH-w)/2,-(HEIGHT-h)/2,0);

 glDisableClientState(GL_COLOR_ARRAY);
 glDisableClientState(GL_VERTEX_ARRAY);
}


void draw_square(int x, int y, float side) {
 float vertexBuffer[12] = {
  0, 0, 0, /*top left*/
  0, side, 0, /*bottom left*/
  side, side, 0, /*bottom right*/
  side, 0, 0 /*top right*/
 };

 glEnableClientState(GL_VERTEX_ARRAY);

 glTranslatef(x-side/2, y-side/2, 0);

 glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

 glTranslatef(-x+side/2, -y+side/2, 0);

 glDisableClientState(GL_VERTEX_ARRAY);
}


模式更改非常有用,尤其是在多显示器环境中。

尝试使用无边界,桌面大小的窗口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <GL/glew.h>
#include <SDL2/SDL.h>

// use border state as proxy for fullscreenedness
SDL_Rect ToggleFakeFullscreen( SDL_Window* window, const SDL_Rect& oldBounds )
{
    if( SDL_GetWindowFlags( window ) & SDL_WINDOW_BORDERLESS )
    {
        SDL_SetWindowBordered( window, SDL_TRUE );
        SDL_SetWindowSize( window, oldBounds.w, oldBounds.h );
        SDL_SetWindowPosition( window, oldBounds.x, oldBounds.y );
        return oldBounds;
    }
    else
    {
        SDL_Rect curBounds;
        SDL_GetWindowPosition( window, &curBounds.x, &curBounds.y );
        SDL_GetWindowSize( window, &curBounds.w, &curBounds.h );

        int idx = SDL_GetWindowDisplayIndex( window );
        SDL_Rect bounds;
        SDL_GetDisplayBounds( idx, &bounds );
        SDL_SetWindowBordered( window, SDL_FALSE );
        SDL_SetWindowPosition( window, bounds.x, bounds.y );
        SDL_SetWindowSize( window, bounds.w, bounds.h );

        return curBounds;
    }
}

int main( int argc, char **argv )
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
        return -1;

    SDL_Window* window = SDL_CreateWindow
        (
       "Test",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        640, 480,
        SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
        );
    if( NULL == window )
        return -1;

    SDL_GLContext ctx = SDL_GL_CreateContext( window );
    if( GLEW_OK != glewInit() )
        return -1;

    SDL_Rect curBounds;

    bool running = true;
    while( running )
    {
        SDL_Event ev;
        while( SDL_WaitEventTimeout( &ev, 16 ) )
        {
            if( ev.type == SDL_QUIT )  
                running = false;
            if( ev.type == SDL_KEYUP &&
                ev.key.keysym.sym == SDLK_ESCAPE )
                running = false;

            if( ev.type == SDL_KEYUP &&
                ev.key.keysym.sym == SDLK_f )
                curBounds = ToggleFakeFullscreen( window, curBounds );
        }

        int w, h;
        SDL_GetWindowSize( window, &w, &h );
        glViewport( 0, 0, w, h );

        glClearColor( 0, 0, 0, 1 );
        glClear( GL_COLOR_BUFFER_BIT );

        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();

        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();

        glColor3ub( 255, 0, 0 );
        glBegin( GL_TRIANGLES );
        glVertex2i( -1, -1 );
        glVertex2i(  1, -1 );
        glVertex2i(  0,  1 );
        glEnd();

        SDL_GL_SwapWindow( window );
    }

    SDL_GL_DeleteContext( ctx );
    SDL_DestroyWindow( window );
    SDL_Quit();

    return 0;
}

点击f切换"全屏"。