gamnit: implement `lock_cursor` using SDL2 `relative_mouse_mode`
[nit.git] / lib / gamnit / display_linux.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Gamnit display implementation for GNU/Linux using `egl`, `sdl` and `x11`
16 module display_linux
17
18 import sdl2::image
19 import sdl2::mixer
20
21 import egl # local to gamnit
22 intrude import display
23 intrude import textures
24
25 redef class GamnitDisplay
26
27 # Actual width or desired width of the window, can be set before calling `setup`
28 fun width=(value: Int) do requested_width = value
29 private var requested_width = 1920
30
31 # Actual height or desired height of the window, can be set before calling `setup`
32 fun height=(value: Int) do requested_height = value
33 private var requested_height = 1080
34
35 redef fun show_cursor do return sdl.show_cursor
36
37 redef fun show_cursor=(val) do sdl.show_cursor = val
38
39 redef fun lock_cursor=(val) do sdl.relative_mouse_mode = val
40
41 redef fun lock_cursor do return sdl.relative_mouse_mode
42
43 # Setup SDL, wm, EGL in order
44 redef fun setup
45 do
46 if debug_gamnit then print "Setting up SDL"
47 self.sdl_window = setup_sdl(requested_width, requested_height)
48
49 if debug_gamnit then print "Setting up window manager"
50 setup_egl_display sdl_window.wm_info.display_handle
51
52 if debug_gamnit then print "Setting up EGL context"
53 select_egl_config(red_bits, green_bits, blue_bits, 8, 8, 0, 0)
54 setup_egl_context sdl_window.wm_info.window_handle
55 end
56
57 # Close EGL and SDL in reverse order of `setup` (nothing to do for X11)
58 redef fun close
59 do
60 close_egl
61 close_sdl
62 end
63
64 # ---
65 # SDL
66
67 # The SDL display managing the window and events
68 var sdl_window: SDLWindow is noautoinit
69
70 # Title of the window, must be set before creating the window (or redef)
71 var window_title = "gamnit game" is lazy, writable
72
73 # Setup the SDL display and lib
74 fun setup_sdl(window_width, window_height: Int): SDLWindow
75 do
76 assert sdl.initialize((new SDLInitFlags).video.audio) else
77 print_error "Failed to initialize SDL2: {sdl.error}"
78 end
79
80 var img_flags = (new SDLImgInitFlags).png.jpg
81 assert sdl.img.initialize(img_flags) == img_flags else
82 print_error "Failed to initialize SDL2 IMG: {sdl.error}"
83 end
84
85 var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, (new SDLWindowFlags).opengl)
86 assert not sdl_window.address_is_null else
87 print_error "Failed to create SDL2 window: {sdl.error}"
88 end
89
90 # Audio support
91 var inited = mix.initialize(mix_init_flags)
92 assert inited != mix_init_flags else
93 print_error "Failed to load SDL2 mixer format supports: {mix.error}"
94 end
95
96 var opened = mix.open_audio(44100, mix.default_format, 2, 1024)
97 assert opened else
98 print_error "Failed to initialize SDL2 mixer: {mix.error}"
99 end
100
101 return sdl_window
102 end
103
104 # Initialization flags passed to SDL2 mixer
105 #
106 # Defaults to all available formats.
107 var mix_init_flags: MixInitFlags = mix.flac | mix.mod | mix.mp3 | mix.ogg is lazy, writable
108
109 # Close the SDL display
110 fun close_sdl
111 do
112 sdl_window.destroy
113 mix.close_audio
114 mix.quit
115 sdl.finalize
116 end
117 end
118
119 redef class TextureAsset
120
121 redef fun load_from_platform
122 do
123 var path = "assets" / path
124
125 var surface = new SDLSurface.load(path.to_cstring)
126 if surface.address_is_null then
127 error = new Error("Failed to load texture at '{path}'")
128 return
129 end
130
131 self.width = surface.w.to_f
132 self.height = surface.h.to_f
133 var format = if surface.format.amask > 0u32 then gl_RGBA else gl_RGB
134 var pixels = surface.pixels
135
136 load_from_pixels(pixels, surface.w, surface.h, format)
137 end
138 end