├── REQUIRE ├── src ├── GLWindow.jl ├── enum.jl ├── glutEvents.jl └── reactglfw.jl ├── test └── runtests.jl ├── .travis.yml ├── LICENSE.md └── README.md /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.3- 2 | GLFW 3 | Reactive 4 | ImmutableArrays 5 | ModernGL 6 | GLAbstraction 7 | Compat 8 | -------------------------------------------------------------------------------- /src/GLWindow.jl: -------------------------------------------------------------------------------- 1 | module GLWindow 2 | 3 | using ModernGL 4 | using GLAbstraction 5 | using GLFW 6 | using Reactive 7 | using ImmutableArrays 8 | using Compat 9 | 10 | import GLFW.Window 11 | import GLFW.Monitor 12 | import GLAbstraction.render 13 | 14 | export createwindow 15 | export Screen 16 | 17 | include("reactglfw.jl") 18 | 19 | 20 | end 21 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using GLWindow, GLFW 2 | using Base.Test 3 | 4 | # write your own tests here 5 | window = createwindow("test", 512,512) 6 | 7 | GLFW.SwapBuffers(window.nativewindow) 8 | while window.inputs[:open].value 9 | yield() 10 | GLFW.PollEvents() 11 | end 12 | GLFW.Terminate() 13 | 14 | 15 | println("\033[32;1mSUCCESS\033[0m") 16 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - clang 4 | notifications: 5 | email: false 6 | env: 7 | matrix: 8 | - JULIAVERSION="juliareleases" 9 | - JULIAVERSION="julianightlies" 10 | before_install: 11 | - sudo add-apt-repository ppa:staticfloat/julia-deps -y 12 | - sudo add-apt-repository ppa:staticfloat/${JULIAVERSION} -y 13 | - sudo apt-get update -qq -y 14 | - sudo apt-get install libpcre3-dev julia -y 15 | script: 16 | - julia -e 'Pkg.init(); Pkg.clone(pwd()); Pkg.test("GLWindow")' 17 | -------------------------------------------------------------------------------- /src/enum.jl: -------------------------------------------------------------------------------- 1 | macro enum(T,syms...) 2 | blk = quote 3 | immutable $(esc(T)) 4 | n::Int32 5 | function $(esc(T))(n::Integer) 6 | if n > length($syms) 7 | error("enum ", $T, " is only defined in the range from 0 to ", $(length(syms))) 8 | end 9 | new(n) 10 | end 11 | end 12 | Base.show(io::IO, x::$(esc(T))) = print(io, $syms[x.n+1]) 13 | Base.show(io::IO, x::Type{$(esc(T))}) = print(io, $(string("enum ", T, ' ', '(', join(syms, ", "), ')'))) 14 | end 15 | for (i,sym) in enumerate(syms) 16 | push!(blk.args, :(const $(esc(sym)) = $(esc(T))($(i-1)))) 17 | end 18 | push!(blk.args, :nothing) 19 | blk.head = :toplevel 20 | return blk 21 | end 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The GLWindow.jl package is licensed under the MIT "Expat" License: 2 | 3 | > Copyright (c) 2014: Simon Danisch. 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLWindow 2 | Simple package to create an OpenGL window. 3 | It also wraps the window events into React signals. 4 | Supposedly more than one window creation library will be suppported, but so far it just creates them with GLFW. 5 | createwindow will return a screen object which basically just wraps all the signals and exposes the handle to the underlying glfw window. 6 | These are the exposed Signals: 7 | ```Julia 8 | Screen.inputs = [ 9 | :insidewindow => Input(false), 10 | :open => Input(true), 11 | 12 | :window_size => window_size(Vector{Float64}(width, height), 13 | :framebuffer_size => Input(Vector2(0)), 14 | :windowposition => Input(Vector2(0)), 15 | 16 | :unicodeinput => Input('0'), 17 | 18 | :buttonspressed => Input(IntSet()),# Set of pressed keyboard keys 19 | :buttondown => Input(0), 20 | :buttonreleased => Input(0), 21 | 22 | :mousebuttonspressed => Input(IntSet()), # Set of pressed mousekeys 23 | :mousedown => Input(0), 24 | :mousereleased => Input(0), 25 | 26 | :mouseposition => mouseposition, 27 | :mouseposition_glfw_coordinates => mouseposition_glfw, 28 | 29 | :scroll_x => Input(0), 30 | :scroll_y => Input(0) 31 | 32 | ] 33 | =# 34 | ``` 35 | -------------------------------------------------------------------------------- /src/glutEvents.jl: -------------------------------------------------------------------------------- 1 | 2 | #push events from glut to our event queue 3 | function keyboardFunc(key::Cuchar, x::Cint, y::Cint) 4 | publishEvent(KeyDown{0}(false, char(key), x, WINDOW_SIZE[2] - y)) 5 | return nothing 6 | end 7 | 8 | function keyboardUpFunc(key::Cuchar, x::Cint, y::Cint) 9 | publishEvent(KeyUp{0}(false, char(key), x, WINDOW_SIZE[2] - y)) 10 | return nothing 11 | end 12 | 13 | 14 | function specialFunc(key::Cint, x::Cint, y::Cint) 15 | publishEvent(KeyDown{0}(true, char(key), x, WINDOW_SIZE[2] - y)) 16 | return nothing 17 | end 18 | 19 | function specialUpFunc(key::Cint, x::Cint, y::Cint) 20 | publishEvent(KeyUp{0}(true, char(key), x, WINDOW_SIZE[2] - y)) 21 | return nothing 22 | end 23 | 24 | 25 | function mouseFunc(button::Cint, status::Cint, x::Cint, y::Cint) 26 | global lastClick = MouseClicked{0}(int(button), int(status), int(x), WINDOW_SIZE[2] - int(y)) 27 | publishEvent(lastClick) 28 | return nothing 29 | end 30 | function motionFunc(x::Cint, y::Cint) 31 | publishEvent(MouseDragged{0}(lastClick, int(x), WINDOW_SIZE[2] - int(y))) 32 | return nothing 33 | end 34 | 35 | function passiveMotionFunc(x::Cint, y::Cint) 36 | publishEvent(MouseMoved{0}(int(x), WINDOW_SIZE[2] - int(y))) 37 | return nothing 38 | end 39 | 40 | function entryFunc(state::Cint) 41 | publishEvent(EnteredWindow{0}(bool(state), glutGetWindow())) 42 | return nothing 43 | end 44 | 45 | 46 | function reshapeFunc(w::Csize_t, h::Csize_t) 47 | WINDOW_SIZE[1] = int(w) 48 | WINDOW_SIZE[2] = int(h) 49 | glViewport(0, 0, w, h) 50 | publishEvent(WindowResized{0}(int(w),int(h))) 51 | return nothing 52 | end 53 | #KeyDownMouseClicked Event generation ############################################################################## 54 | 55 | global currentMouseClicked = Dict{Int, (Int, Int)}() 56 | global currentKeyDown = Dict{Int, Bool}() 57 | 58 | function fillCurrentMouseClicked(event) 59 | if event.status == 0 60 | currentMouseClicked[int(event.key)] = (int(event.x), int(event.y)) 61 | if ~isempty(currentKeyDown) 62 | publishEvent(KeyDownMouseClicked(deepcopy(currentMouseClicked), deepcopy(currentKeyDown), int(event.x), int(event.y))) 63 | end 64 | else 65 | pop!(currentMouseClicked, int(event.key), ()) 66 | end 67 | end 68 | function fillCurrentKeyDown(event, status::Int) 69 | if status == 1 70 | currentKeyDown[int(event.key)] = event.special 71 | if ~isempty(currentMouseClicked) 72 | publishEvent(KeyDownMouseClicked(deepcopy(currentMouseClicked), deepcopy(currentKeyDown), int(event.x), int(event.y))) 73 | end 74 | else 75 | pop!(currentKeyDown, int(event.key), ()) 76 | end 77 | end 78 | registerEventAction(EventAction{KeyDown} (x-> true, (), fillCurrentKeyDown, (1,))) 79 | registerEventAction(EventAction{KeyUp} (x-> true, (), fillCurrentKeyDown, (0,))) 80 | registerEventAction(EventAction{MouseClicked} (x-> true, (), fillCurrentMouseClicked, ())) 81 | 82 | 83 | left_click_down(event::MouseClicked) = event.key == 0 && event.status == 0 84 | 85 | middle_click_down_inside(event::MouseDragged, rect) = event.start.key == 1 && event.start.status == 0 && inside(rect, event.start.x, event.start.y) 86 | right_click_down_inside(event::MouseDragged, rect) = event.start.key == 2 && event.start.status == 0 && inside(rect, event.start.x, event.start.y) 87 | 88 | left_click_up(event::MouseClicked) = event.key == 0 && event.status == 1 89 | 90 | 91 | #Cfunction pointer for glut 92 | _entryFunc = cfunction(entryFunc, Void, (Cint,)) 93 | _motionFunc = cfunction(motionFunc, Void, (Cint, Cint)) 94 | _passiveMotionFunc = cfunction(passiveMotionFunc, Void, (Cint, Cint)) 95 | _mouseFunc = cfunction(mouseFunc, Void, (Cint, Cint, Cint, Cint)) 96 | _specialFunc = cfunction(specialFunc, Void, (Cint, Cint, Cint)) 97 | _specialUpFunc = cfunction(specialUpFunc, Void, (Cint, Cint, Cint)) 98 | _keyboardFunc = cfunction(keyboardFunc, Void, (Cuchar, Cint, Cint)) 99 | _keyboardUpFunc = cfunction(keyboardUpFunc, Void, (Cuchar, Cint, Cint)) 100 | _reshapeFunc = cfunction(reshapeFunc, Void, (Csize_t, Csize_t)) 101 | _displayFunc = cfunction(displayFunc, Void, ()) 102 | _closeFunc = cfunction(closeFunc, Void, ()) 103 | 104 | 105 | function displayFunc() 106 | glClearColor(1f0, 1f0, 1f0, 1f0) 107 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 108 | for elem in RENDER_LIST 109 | render(elem) 110 | end 111 | for elem in RENDER_DICT 112 | render(elem[2]...) 113 | end 114 | 115 | glutSwapBuffers() 116 | return nothing 117 | end 118 | 119 | function createWindow(; 120 | name = "GLUT Window", 121 | displayMode = convert(Cint, (GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE | GLUT_ALPHA | GLUT_STENCIL)), 122 | windowPosition = Cint[0,0], 123 | windowSize = Cint[1000,1000], 124 | displayF=true, idleF=true, reshapeF=true, 125 | entryF=true, keyboardF=true, specialF=true, 126 | keyboardUpF=true, specialUpF=true, mouseF=true, 127 | motionF=true, passiveMotionF=true) 128 | 129 | glutInit() 130 | glutInitDisplayMode(displayMode) 131 | glutInitWindowPosition(windowPosition...) 132 | glutInitWindowSize(windowSize...) 133 | window = glutCreateWindow(name) 134 | 135 | initGLUtils() 136 | 137 | 138 | displayF && glutDisplayFunc (_displayFunc) 139 | idleF && glutIdleFunc (_displayFunc) 140 | reshapeF && glutReshapeFunc (_reshapeFunc) 141 | keyboardF && glutKeyboardFunc (_keyboardFunc) 142 | specialF && glutSpecialFunc (_specialFunc) 143 | keyboardUpF && glutKeyboardUpFunc (_keyboardUpFunc) 144 | specialUpF && glutSpecialUpFunc (_specialUpFunc) 145 | mouseF && glutMouseFunc (_mouseFunc) 146 | motionF && glutMotionFunc (_motionFunc) 147 | passiveMotionF && glutPassiveMotionFunc (_passiveMotionFunc) 148 | entryF && glutEntryFunc (_entryFunc) 149 | glutCloseFunc(_closeFunc) 150 | 151 | #(keyboardUpF | specialUpF) && glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF) 152 | window 153 | end 154 | 155 | export left_click_up, right_click_down_inside, middle_click_down_inside, left_click_down -------------------------------------------------------------------------------- /src/reactglfw.jl: -------------------------------------------------------------------------------- 1 | 2 | immutable MonitorProperties 3 | name::ASCIIString 4 | isprimary::Bool 5 | position::Vector2 6 | physicalsize::Vector2 7 | #gamma::Float64 8 | gammaramp::GLFW.GammaRamp 9 | videomode::GLFW.VidMode 10 | videomode_supported::Vector{GLFW.VidMode} 11 | dpi::Vector2 12 | monitor::Monitor 13 | end 14 | 15 | function MonitorProperties(monitor::Monitor) 16 | name = GLFW.GetMonitorName(monitor) 17 | isprimary = GLFW.GetPrimaryMonitor() == monitor 18 | position = Vector2(GLFW.GetMonitorPos(monitor)...) 19 | physicalsize = Vector2(GLFW.GetMonitorPhysicalSize(monitor)...) 20 | gammaramp = GLFW.GetGammaRamp(monitor) 21 | videomode = GLFW.GetVideoMode(monitor) 22 | 23 | dpi = Vector2(videomode.width * 25.4, videomode.height * 25.4) ./ physicalsize 24 | videomode_supported = GLFW.GetVideoModes(monitor) 25 | 26 | MonitorProperties(name, isprimary, position, physicalsize, gammaramp, videomode, videomode_supported, dpi, monitor) 27 | end 28 | 29 | function Base.show(io::IO, m::MonitorProperties) 30 | println(io, "name: ", m.name) 31 | println(io, "physicalsize: ", m.physicalsize[1], "x", m.physicalsize[2]) 32 | println(io, "resolution: ", m.videomode.width, "x", m.videomode.height) 33 | println(io, "dpi: ", m.dpi[1], "x", m.dpi[2]) 34 | end 35 | 36 | type Screen 37 | id::Symbol 38 | area 39 | parent::Screen 40 | children::Vector{Screen} 41 | inputs::Dict{Symbol, Any} 42 | renderlist::Vector{RenderObject} 43 | 44 | hidden::Signal{Bool} 45 | hasfocus::Signal{Bool} 46 | 47 | perspectivecam::PerspectiveCamera 48 | orthographiccam::OrthographicCamera 49 | nativewindow::Window 50 | counter = 1 51 | function Screen( 52 | area, 53 | parent::Screen, 54 | children::Vector{Screen}, 55 | inputs::Dict{Symbol, Any}, 56 | renderlist::Vector{RenderObject}, 57 | 58 | hidden::Signal{Bool}, 59 | hasfocus::Signal{Bool}, 60 | 61 | perspectivecam::PerspectiveCamera, 62 | orthographiccam::OrthographicCamera, 63 | nativewindow::Window) 64 | new(symbol("display"*string(counter+=1)), area, parent, children, inputs, renderlist, hidden, hasfocus, perspectivecam, orthographiccam, nativewindow) 65 | end 66 | 67 | function Screen( 68 | area, 69 | children::Vector{Screen}, 70 | inputs::Dict{Symbol, Any}, 71 | renderlist::Vector{RenderObject}, 72 | 73 | hidden::Signal{Bool}, 74 | hasfocus::Signal{Bool}, 75 | 76 | perspectivecam::PerspectiveCamera, 77 | orthographiccam::OrthographicCamera, 78 | nativewindow::Window) 79 | parent = new() 80 | new(symbol("display"*string(counter+=1)), area, parent, children, inputs, renderlist, hidden, hasfocus, perspectivecam, orthographiccam, nativewindow) 81 | end 82 | end 83 | 84 | #Screen constructor 85 | function Screen( 86 | parent::Screen; 87 | area = parent.area, 88 | children::Vector{Screen} = Screen[], 89 | inputs::Dict{Symbol, Any} = parent.inputs, 90 | renderlist::Vector{RenderObject} = RenderObject[], 91 | 92 | hidden::Signal{Bool} = parent.hidden, 93 | hasfocus::Signal{Bool} = parent.hasfocus, 94 | 95 | nativewindow::Window = parent.nativewindow) 96 | #checks if mouse is inside screen 97 | insidescreen = lift(inputs[:mouseposition]) do mpos 98 | isinside(area.value, mpos...) && !any(children) do screen 99 | isinside(screen.area.value, mpos...) 100 | end 101 | end 102 | # creates signals for the camera, which are only active if mouse is inside screen 103 | camera_input = merge(inputs, @compat(Dict( 104 | :mouseposition => keepwhen(insidescreen, Vector2(0.0), inputs[:mouseposition]), 105 | :scroll_x => keepwhen(insidescreen, 0, inputs[:scroll_x]), 106 | :scroll_y => keepwhen(insidescreen, 0, inputs[:scroll_y]), 107 | :window_size => lift(x->Vector4(x.x, x.y, x.w, x.h), area) 108 | ))) 109 | # creates cameras for the sceen with the new inputs 110 | ocamera = OrthographicPixelCamera(camera_input) 111 | pcamera = PerspectiveCamera(camera_input, Vec3(2), Vec3(0)) 112 | screen = Screen(area, parent, children, inputs, renderlist, hidden, hasfocus, pcamera, ocamera, nativewindow) 113 | push!(parent.children, screen) 114 | screen 115 | end 116 | function GLAbstraction.isinside(x::Screen, position::Vector2) 117 | !any(screen->inside(screen.area.value, position...), x.children) && inside(x.area, position...) 118 | end 119 | 120 | function Screen(obj::RenderObject, parent::Screen) 121 | 122 | area = boundingbox2D(obj) 123 | hidden = Input(false) 124 | screen = Screen(parent) 125 | mouse = filter(inside, Input(Screen), parent.inputs[:mouseposition]) 126 | 127 | hasfocus = lift(parent.inputs[:mouseposition], parent.inputs[:mousebuttonpressed], screen.area) do pos, buttons, area 128 | isinside(area, pos...) && !isempty(bottons) 129 | end 130 | buttons = menubar(screen) 131 | push!(parent.children, screen) 132 | push!(screen.renderlist, buttons) 133 | push!(screen.renderlist, obj) 134 | end 135 | 136 | function Screen(style::Style{:Default}, parent=first(SCREEN_STACK)) 137 | 138 | hidden = Input(true) 139 | screen = Screen(parent) 140 | mouse = filter(Input(Screen), parent.inputs[:mouseposition]) do screen, mpos 141 | end 142 | inputs = merge(parent.inputs, @compat(Dict(:mouseposition=>mouse))) 143 | opxcamera = OrthographicPixelCamera(inputs) 144 | pcamera = PerspectiveCamera(inputs) 145 | hasfocus = lift(parent.inputs[:mouseposition], parent.inputs[:mousebuttonpressed], screen.area) do pos, buttons, area 146 | isinside(area, pos...) && !isempty(bottons) 147 | end 148 | screen = Screen(area, parent, children=Screen[], inputs, renderList, hidden, hasfocus, perspectivecam, orthographiccam) 149 | buttons = menubar(screen, style) 150 | 151 | push!(parent.children, screen) 152 | push!(screen.renderlist, buttons) 153 | 154 | end 155 | 156 | dict = Dict{Symbol, Vec4}() 157 | function GLAbstraction.render(x::Screen) 158 | glEnable(GL_SCISSOR_TEST) 159 | glScissor(x.area.value.x, x.area.value.y, x.area.value.w, x.area.value.h) 160 | glViewport(x.area.value) 161 | 162 | render(x.renderlist) 163 | render(x.children) 164 | end 165 | function Base.show(io::IO, m::Screen) 166 | println(io, "name: ", m.id) 167 | println(io, "children: ", length(m.children)) 168 | println(io, "Inputs:") 169 | map(m.inputs) do x 170 | key, value = x 171 | println(io, " ", key, " => ", typeof(value)) 172 | end 173 | end 174 | 175 | const WINDOW_TO_SCREEN_DICT = Dict{Window, Screen}() 176 | const GLFW_SCREEN_STACK = Screen[] 177 | 178 | 179 | import Base.(==) 180 | Base.hash(x::Window, h::Int64) = hash(convert(Uint, x.ref), h) 181 | Base.isequal(a::Window, b::Window) = isequal(convert(Uint, a.ref), convert(Uint, b.ref)) 182 | ==(a::Window, b::Window) = convert(Uint, a.ref) == convert(Uint, b.ref) 183 | 184 | function update(window::Window, key::Symbol, value; keepsimilar = false) 185 | if haskey(WINDOW_TO_SCREEN_DICT, window) 186 | screen = WINDOW_TO_SCREEN_DICT[window] 187 | input = screen.inputs[key] 188 | if keepsimilar || input.value != value 189 | push!(input, value) 190 | end 191 | end 192 | end 193 | 194 | function window_closed(window) 195 | update(window, :open, false) 196 | return nothing 197 | end 198 | 199 | function window_resized(window, w::Cint, h::Cint) 200 | update(window, :window_size, Vector4{Int}(0, 0, w, h)) 201 | return nothing 202 | end 203 | function framebuffer_size(window, w::Cint, h::Cint) 204 | update(window, :framebuffer_size, Vector2{Int}(w, h)) 205 | return nothing 206 | end 207 | function window_position(window, x::Cint, y::Cint) 208 | update(window, :windowposition, Vector2{Int}(x,y)) 209 | return nothing 210 | end 211 | 212 | 213 | 214 | function key_pressed(window::Window, button::Cint, scancode::Cint, action::Cint, mods::Cint) 215 | screen = WINDOW_TO_SCREEN_DICT[window] 216 | if sign(button) == 1 217 | buttonspressed = screen.inputs[:buttonspressed] 218 | keyset = buttonspressed.value 219 | buttonI = @compat(Int(button)) 220 | if action == GLFW.PRESS 221 | buttondown = screen.inputs[:buttondown] 222 | push!(buttondown, buttonI) 223 | push!(keyset, buttonI) 224 | push!(buttonspressed, keyset) 225 | elseif action == GLFW.RELEASE 226 | buttonreleased = screen.inputs[:buttonreleased] 227 | push!(buttonreleased, buttonI) 228 | setdiff!(keyset, Set(buttonI)) 229 | push!(buttonspressed, keyset) 230 | end 231 | end 232 | return nothing 233 | end 234 | function mouse_clicked(window::Window, button::Cint, action::Cint, mods::Cint) 235 | screen = WINDOW_TO_SCREEN_DICT[window] 236 | 237 | buttonspressed = screen.inputs[:mousebuttonspressed] 238 | keyset = buttonspressed.value 239 | buttonI = @compat(Int(button)) 240 | if action == GLFW.PRESS 241 | buttondown = screen.inputs[:mousedown] 242 | push!(buttondown, buttonI) 243 | push!(keyset, buttonI) 244 | push!(buttonspressed, keyset) 245 | elseif action == GLFW.RELEASE 246 | buttonreleased = screen.inputs[:mousereleased] 247 | push!(buttonreleased, buttonI) 248 | setdiff!(keyset, Set(buttonI)) 249 | push!(buttonspressed, keyset) 250 | end 251 | return nothing 252 | end 253 | 254 | function unicode_input(window::Window, c::Cuint) 255 | update(window, :unicodeinput, Char[c], keepsimilar = true) 256 | update(window, :unicodeinput, Char[], keepsimilar = true) 257 | return nothing 258 | end 259 | 260 | function cursor_position(window::Window, x::Cdouble, y::Cdouble) 261 | update(window, :mouseposition_glfw_coordinates, Vector2{Float64}(x, y)) 262 | return nothing 263 | end 264 | function hasfocus(window::Window, focus::Cint) 265 | update(window, :hasfocus, focus==GL_TRUE) 266 | return nothing 267 | end 268 | function scroll(window::Window, xoffset::Cdouble, yoffset::Cdouble) 269 | screen = WINDOW_TO_SCREEN_DICT[window] 270 | push!(screen.inputs[:scroll_x], @compat(Float64(xoffset))) 271 | push!(screen.inputs[:scroll_y], @compat(Float64(yoffset))) 272 | push!(screen.inputs[:scroll_x], zero(Float64)) 273 | push!(screen.inputs[:scroll_y], zero(Float64)) 274 | return nothing 275 | end 276 | function entered_window(window::Window, entered::Cint) 277 | update(window, :insidewindow, entered == 1) 278 | return nothing 279 | end 280 | 281 | 282 | function openglerrorcallback( 283 | source::GLenum, typ::GLenum, 284 | id::GLuint, severity::GLenum, 285 | length::GLsizei, message::Ptr{GLchar}, 286 | userParam::Ptr{Void} 287 | ) 288 | errormessage = "\n"* 289 | " ________________________________________________________________\n"* 290 | "|\n"* 291 | "| OpenGL Error!\n"* 292 | "| source: $(GLENUM(source).name) :: type: $(GLENUM(typ).name)\n"* 293 | "| "*ascii(bytestring(message, length))*"\n"* 294 | "|________________________________________________________________\n" 295 | if typ == GL_DEBUG_TYPE_ERROR 296 | error(errormessage) 297 | end 298 | nothing 299 | end 300 | 301 | 302 | 303 | global const _openglerrorcallback = cfunction(openglerrorcallback, Void, 304 | (GLenum, GLenum, 305 | GLuint, GLenum, 306 | GLsizei, Ptr{GLchar}, 307 | Ptr{Void})) 308 | 309 | function createwindow(name::String, w, h; debugging = false, windowhints=[(GLFW.SAMPLES, 4)]) 310 | GLFW.Init() 311 | for elem in windowhints 312 | GLFW.WindowHint(elem...) 313 | end 314 | @osx_only begin 315 | if debugging 316 | println("warning: OpenGL debug message callback not available on osx") 317 | debugging = false 318 | end 319 | GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, 3) 320 | GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, 2) 321 | GLFW.WindowHint(GLFW.OPENGL_FORWARD_COMPAT, GL_TRUE) 322 | GLFW.WindowHint(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE) 323 | end 324 | 325 | GLFW.WindowHint(GLFW.OPENGL_DEBUG_CONTEXT, convert(Cint, debugging)) 326 | window = GLFW.CreateWindow(w, h, name) 327 | GLFW.MakeContextCurrent(window) 328 | GLFW.ShowWindow(window) 329 | if debugging 330 | glDebugMessageCallbackARB(_openglerrorcallback, C_NULL) 331 | end 332 | 333 | GLFW.SetWindowCloseCallback(window, window_closed) 334 | GLFW.SetWindowSizeCallback(window, window_resized) 335 | GLFW.SetWindowPosCallback(window, window_position) 336 | GLFW.SetKeyCallback(window, key_pressed) 337 | GLFW.SetCharCallback(window, unicode_input) 338 | GLFW.SetMouseButtonCallback(window, mouse_clicked) 339 | GLFW.SetCursorPosCallback(window, cursor_position) 340 | GLFW.SetScrollCallback(window, scroll) 341 | GLFW.SetCursorEnterCallback(window, entered_window) 342 | GLFW.SetFramebufferSizeCallback(window, framebuffer_size) 343 | GLFW.SetWindowFocusCallback(window, hasfocus) 344 | 345 | GLFW.SetWindowSize(window, w, h) # Seems to be necessary to guarantee that window > 0 346 | 347 | width, height = GLFW.GetWindowSize(window) 348 | fwidth, fheight = GLFW.GetFramebufferSize(window) 349 | framebuffers = Input(Vector2{Int}(fwidth, fheight)) 350 | window_size = Input(Vector4{Int}(0, 0, width, height)) 351 | glViewport(0, 0, fwidth, fheight) 352 | 353 | 354 | mouseposition_glfw = Input(Vector2(0.0)) 355 | mouseposition = lift((mouse, window) -> Vector2(mouse[1], window[4] - mouse[2]), Vector2{Float64}, mouseposition_glfw, window_size) 356 | 357 | 358 | inputs = Dict{Symbol, Any}() 359 | inputs[:insidewindow] = Input(false) 360 | inputs[:open] = Input(true) 361 | inputs[:hasfocus] = Input(false) 362 | 363 | inputs[:window_size] = window_size 364 | inputs[:framebuffer_size] = framebuffers 365 | inputs[:windowposition] = Input(Vector2(0)) 366 | 367 | inputs[:unicodeinput] = Input(Char[]) 368 | 369 | inputs[:buttonspressed] = Input(IntSet()) 370 | inputs[:buttondown] = Input(0) 371 | inputs[:buttonreleased] = Input(0) 372 | 373 | inputs[:mousebuttonspressed] = Input(IntSet()) 374 | inputs[:mousedown] = Input(0) 375 | inputs[:mousereleased] = Input(0) 376 | 377 | inputs[:mouseposition] = mouseposition 378 | inputs[:mouseposition_glfw_coordinates] = mouseposition_glfw 379 | 380 | inputs[:scroll_x] = Input(0.0) 381 | inputs[:scroll_y] = Input(0.0) 382 | 383 | children = Screen[] 384 | mouse = filter(Vector2(0.0), mouseposition) do mpos 385 | !any(children) do screen 386 | isinside(screen.area.value, mpos...) 387 | end 388 | end 389 | camera_input = merge(inputs, @compat(Dict(:mouseposition=>mouse))) 390 | pcamera = PerspectiveCamera(camera_input, Vec3(2), Vec3(0)) 391 | pocamera = OrthographicPixelCamera(camera_input) 392 | 393 | screen = Screen(lift(x->Rectangle(0, 0, x...), framebuffers), children, inputs, RenderObject[], Input(false), inputs[:hasfocus], pcamera, pocamera, window) 394 | WINDOW_TO_SCREEN_DICT[window] = screen 395 | push!(GLFW_SCREEN_STACK, screen) 396 | 397 | init_glutils() 398 | screen 399 | end 400 | --------------------------------------------------------------------------------