1import ctypes
2import os
3import sys
4import time
5from collections import deque
6
7from arcade.gamecentersettings import ArcadeSettings
8from fsbc import settings
9from fsgs.FSGSDirectories import FSGSDirectories
10
11from arcade.gamecenter import GameCenter
12from arcade.resources import resources
13from fsbc.system import windows
14from fsgs.option import Option
15from fsgs.platform import PlatformHandler
16from fsgs.util.gamenameutil import GameNameUtil
17
18# from arcade.main import Main
19from arcade.resources import logger
20from arcade.glui.opengl import gl, fs_emu_texturing, fs_emu_blending
21from arcade.glui.settings import Settings
22from arcade.glui.sdl import SDL_IsMinimized
23from arcade.glui.imageloader import ImageLoader
24from arcade.glui.itemmenu import ItemMenu
25from arcade.glui.displaylists import DisplayLists
26from arcade.glui.bezier import Bezier
27from arcade.glui.input import InputHandler
28from arcade.glui.animation import AnimationSystem
29from arcade.glui.animation import AnimateValueBezier
30from arcade.glui.state import State
31from arcade.glui.render import Render
32from arcade.glui.notificationrender import NotificationRender
33from arcade.glui.font import Font, BitmapFont
34from arcade.glui.items import MenuItem, AllMenuItem, NoItem, PlatformItem
35from arcade.glui.items import ListItem
36from arcade.glui.texture import Texture
37from arcade.glui.texturemanager import TextureManager
38from arcade.glui.constants import TOP_HEIGHT
39
40# FIXME: rename to manager or director or somethign
41main_window = None
42
43# ENABLE_VSYNC = Config.get_bool("Menu/VSync", True)
44ENABLE_VSYNC = "--vsync" in sys.argv
45ALWAYS_RENDER = True
46# IDLE = Config.get_bool("Menu/Idle", 1)
47IDLE = 1
48if "--no-idle" in sys.argv:
49    IDLE = 0
50# Render.get().display_fps = pyapp.user.ini.get_bool("Menu/ShowFPS", False)
51# LIGHTING = Config.get_bool("Menu/Lighting", False)
52LIGHTING = False
53RENDER_DEBUG_SQUARES = 0
54if "--render-debug-squares" in sys.argv:
55    RENDER_DEBUG_SQUARES = 1
56SEARCH_CHARS = (
57    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 -:.,"
58)
59CONFIG_SEPARATION = 0.15
60ANIMATION_SETTLE_TIME_1 = 1.0
61ANIMATION_SETTLE_TIME_2 = 2.0
62
63force_display_ratio = None
64display = None
65real_display_height = 0
66current_menu = None
67last_menu = None
68last_game = None
69
70
71def get_current_time():
72    return time.time()
73
74
75def set_current_menu(menu):
76    if not isinstance(menu, SearchResultsMenu):
77        menu.search_text = ""
78
79    global current_menu
80    if len(menu) == 0:
81        menu.append(NoItem())
82    current_menu = menu
83    State.get().current_menu = menu
84    if menu.navigatable:
85        State.get().navigatable = menu.navigatable
86    else:
87        State.get().navigatable = menu
88    State.get().top_menu = menu.top
89    State.get().down_navigatable = None
90    Render.get().dirty = True
91
92
93# WINDOWED_SIZE = [1024, 640]  # 16:10
94# WINDOWED_SIZE = (1024, 576)  # 16:9
95# WINDOWED_SIZE = [1024, 768]  # 4:3
96# WINDOWED_SIZE = [960, 540]  # 16:9
97WINDOWED_SIZE = [1280, 720]  # 16:9
98
99RENDER_GAME = ["IMAGE", "REFLECTIONS", "SCREENSHOTS"]
100RENDER_GAME_OVERLAY = ["TITLE", "HEADER"]
101
102# USE_MENU_TRANSITIONS = pyapp.user.ini.get_bool("Menu/Transitions", True)
103# if "--disable-shaders" in sys.argv:
104#     USE_MENU_TRANSITIONS = False
105USE_MENU_TRANSITIONS = False
106FIELD_COLOR = 0.1
107WALL_COLOR = 0.0
108# IDLE_EVENT = pygame.NUMEVENTS - 1
109DEFAULT_ITEM_BRIGHTNESS = 0.8
110
111
112class MenuGameTransition(object):
113    value = 0.0
114
115
116class MenuForwardTransition(object):
117    # must be initially set to 1 because this indicates that
118    # the transition is done / not in progress
119    value = 1.0
120
121
122class MenuBackwardTransition(object):
123    # must be initially set to 1 because this indicates that
124    # the transition is done / not in progress
125    value = 1.0
126
127
128class ScreenshotTransition(object):
129    anim = None
130    # must be initially set to 1 because this indicates that
131    # the transition is done / not in progress
132    value = 1.0
133
134
135class RunTransition(object):
136    value = 0.0
137
138
139class Mouse(object):
140    items = []
141    focus = None
142
143    @classmethod
144    def set_visible(cls, visible=True):
145        assert visible is not None
146        # if State.get().mouse_visible == visible:
147        #     return
148        # if not visible:
149        #     cls.focus = None
150        # pygame.mouse.set_visible(visible)
151        # State.get().mouse_visible = visible
152        print("Mouse.set_visible not implemented")
153
154
155def set_items_brightness(brightness, duration=1.0, delay=0.0):
156    State.get().items_brightness_anim = AnimateValueBezier(
157        (State, "items_brightness"),
158        State.get().items_brightness,
159        State.get().time + delay,
160        State.get().items_brightness,
161        State.get().time + delay,
162        brightness,
163        State.get().time + delay + duration,
164        brightness,
165        State.get().time + delay + duration,
166    )
167
168
169def compile_shader(source, shader_type):
170    shader = gl.glCreateShaderObjectARB(shader_type)
171    gl.glShaderSourceARB(shader, source)
172    gl.glCompileShaderARB(shader)
173    try:
174        status = ctypes.c_int()
175        gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS, ctypes.byref(status))
176        status = status.value
177    except TypeError:
178        status = gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS)
179    if not status:
180        print_log(shader)
181        gl.glDeleteObjectARB(shader)
182        raise ValueError("Shader compilation failed")
183    return shader
184
185
186def compile_program(vertex_source, fragment_source):
187    vertex_shader = None
188    fragment_shader = None
189    program = gl.glCreateProgram()
190    if vertex_source:
191        print("compile vertex shader")
192        vertex_shader = compile_shader(vertex_source, gl.GL_VERTEX_SHADER)
193        gl.glAttachShader(program, vertex_shader)
194    if fragment_source:
195        print("compile fragment shader")
196        fragment_shader = compile_shader(
197            fragment_source, gl.GL_FRAGMENT_SHADER
198        )
199        gl.glAttachShader(program, fragment_shader)
200    gl.glLinkProgram(program)
201    status = gl.glGetProgramiv(program, gl.GL_LINK_STATUS)
202    if status == gl.GL_FALSE:
203        print("could not link shader program")
204        print(gl.glGetProgramInfoLog(program))
205        sys.exit(1)
206    if vertex_shader:
207        gl.glDeleteShader(vertex_shader)
208    if fragment_shader:
209        gl.glDeleteShader(fragment_shader)
210    return program
211
212
213def print_log(shader):
214    length = ctypes.c_int()
215    gl.glGetShaderiv(shader, gl.gl.GL_INFO_LOG_LENGTH, ctypes.byref(length))
216    if length.value > 0:
217        shader_log = gl.glGetShaderInfoLog(shader)
218        print(shader_log)
219        sys.exit(1)
220
221
222texture_program = None
223color_program = None
224premultiplied_texture_program = None
225
226
227def compile_programs():
228    global texture_program, color_program, premultiplied_texture_program
229    vertex_shader = None
230
231    color_program = compile_program(
232        vertex_shader,
233        [
234            """
235void main()
236{
237vec4 s = gl_Color;
238float extra_alpha = 1.0;
239//float extra_alpha = gl_TextureEnvColor[0].a;
240float a = s.a * extra_alpha;
241gl_FragColor.a = a;
242gl_FragColor.r = s.r*a;
243gl_FragColor.g = s.g*a;
244gl_FragColor.b = s.b*a;
245}
246            """
247        ],
248    )
249
250    texture_program = compile_program(
251        vertex_shader,
252        [
253            """
254uniform sampler2D texture;
255
256void main()
257{
258vec4 s = texture2D(texture,gl_TexCoord[0].st);
259//float extra_alpha = 1.0;
260//float extra_alpha = gl_TextureEnvColor[0].a;
261float opacity = gl_Color.a;
262float a = s.a * opacity;
263gl_FragColor.a = a;
264gl_FragColor.r = s.r * a;
265gl_FragColor.g = s.g * a;
266gl_FragColor.b = s.b * a;
267}
268"""
269        ],
270    )
271
272    premultiplied_texture_program = compile_program(
273        vertex_shader,
274        [
275            """
276uniform sampler2D texture;
277
278void main()
279{
280vec4 s = texture2D(texture,gl_TexCoord[0].st);
281//float extra_alpha = 1.0;
282//float extra_alpha = gl_TextureEnvColor[0].a;
283float opacity = gl_Color.a;
284gl_FragColor.a = s.a * opacity;
285gl_FragColor.r = s.r * opacity;
286gl_FragColor.g = s.g * opacity;
287gl_FragColor.b = s.b * opacity;
288}
289"""
290        ],
291    )
292
293
294def enable_texture_shader():
295    fs_emu_blending(True)
296    gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
297    gl.glUseProgram(texture_program)
298
299
300def disable_shader():
301    gl.glUseProgram(0)
302
303
304def set_program(program):
305    if not program:
306        gl.glUseProgram(0)
307    else:
308        gl.glUseProgram(program)
309
310
311def enter_menu(result, replace=False):
312    print("enter_menu", result, "replace", replace)
313    print("   menu parent_menu", result.parent_menu)
314
315    if replace:
316        State.get().history.pop()
317    c_menu = State.get().history[-1]
318    result.parents[:] = c_menu.parents
319    result.parents.append(c_menu)
320    print("   menu parents    ", result.parents)
321    State.get().history.append(result)
322    set_current_menu(result)
323
324
325def create_main_menu():
326    new_menu = AllMenuItem().activate(None)
327    if len(new_menu) == 0:
328        new_menu.append(NoItem())
329    return new_menu
330
331
332def recreate_main_menu_if_necessary():
333    new_menu = create_main_menu()
334    State.get().history.append(new_menu)
335    set_current_menu(new_menu)
336
337
338def show():
339    global current_menu
340
341    # fade_from is used on init_display, so we initialize this
342    # color here. Set alpha to 2.0 to force 1 second of solid
343    # color in combination with 2 sec. animation below
344    if False and windows and not Settings.fullscreen_menu:
345        State.get().fade_from = (1.0, 1.0, 1.0, 2.0)
346        State.get().fade_to = (1.0, 1.0, 1.0, 0.0)
347    else:
348        State.get().fade_from = (0.0, 0.0, 0.0, 2.0)
349        State.get().fade_to = (0.0, 0.0, 0.0, 0.0)
350    init_display()
351    if LIGHTING:
352        init_lighting()
353    init_textures()
354    init_fonts()
355
356    if USE_MENU_TRANSITIONS:
357        compile_programs()
358
359    InputHandler.open()
360
361    on_resize((Render.get().display_width, Render.get().display_height))
362    image_loader = ImageLoader.get()
363    image_loader.start()
364    new_menu = create_main_menu()
365    State.get().history.append(new_menu)
366
367    override_items = []
368    for platform_id in PlatformHandler.get_platform_ids():
369        if "--platform=" + platform_id in sys.argv:
370            sys.argv.remove("--platform=" + platform_id)
371            platform_item = PlatformItem(platform_id)
372            override_items.append(platform_item)
373            break
374    # if "--favorites" in sys.argv or \
375    #         settings.get(Option.ARCADE_INITIAL_VIEW) == "favorites":
376    if (
377        "--favorites" in sys.argv
378        or settings.get(Option.ARCADE_INITIAL_FAVORITES) == "1"
379    ):
380        if "--favorites" in sys.argv:
381            sys.argv.remove("--favorites")
382        try:
383            list_uuid = ListItem.get_favorites_uuid()
384            favorites_item = ListItem("Favorites", list_uuid)
385        except LookupError:
386            print("No favorites list")
387            # noinspection SpellCheckingInspection
388            favorites_item = ListItem(
389                "Favorites", "c03dd5fe-0e85-4efb-a126-f0e4f40feae6"
390            )
391        override_items.append(favorites_item)
392    if override_items:
393        parent_menu = new_menu
394        override_menu = None
395        parents = []
396        for item in override_items:
397            override_menu = ItemMenu()
398            override_menu.parent_menu = parent_menu
399            parents.append(parent_menu)
400            # This is really hackish
401            override_menu.parents = parents
402            override_menu.items.append(item)
403            parent_menu = override_menu
404        new_menu = override_items[-1].activate(override_menu)
405        print(new_menu)
406        State.get().history.append(new_menu)
407
408    set_current_menu(new_menu)
409    if len(new_menu) == 1:
410        # only n/a item showing, possibly
411        if len(AllMenuItem().activate(None)) == 0:
412            # no games, initiate game scan
413            rescan_games()
414
415    State.get().fade_start = get_current_time()
416    State.get().fade_end = get_current_time() + 2.000
417
418    # # make a timer so that update events are sent to modules at least once
419    # # every second
420    # pygame.time.set_timer(IDLE_EVENT, 1000)
421
422    State.get().start_time = get_current_time()
423
424
425def init_fonts():
426    NotificationRender.init()
427
428    BitmapFont.title_font = BitmapFont("title_font")
429    BitmapFont.menu_font = BitmapFont("menu_font")
430
431
432liberation_sans_bold_path = None
433liberation_sans_narrow_bold_path = None
434vera_font_path = None
435
436
437def find_font_path_or_stream(name):
438    path = os.path.join(
439        FSGSDirectories.get_base_dir(), "Workspace", "Fonts", name
440    )
441    if os.path.exists(path):
442        return path
443    stream = resources.resource_stream(name)
444    return stream
445
446
447def reinit_fonts():
448    print(
449        "reinit_fonts, Render.get().display_height =",
450        Render.get().display_height,
451    )
452    global liberation_sans_bold_path
453    global liberation_sans_narrow_bold_path
454    global vera_font_path
455
456    if liberation_sans_bold_path is None:
457        liberation_sans_bold_path = find_font_path_or_stream(
458            "LiberationSans-Bold.ttf"
459        )
460
461    # if liberation_sans_narrow_bold_path is None:
462    #     liberation_sans_narrow_bold_path = resources.resource_filename(
463    #         "LiberationSansNarrow-Bold.ttf")
464    # if vera_font_path is None:
465    #     vera_font_path = resources.resource_filename(
466    #         "VeraBd.ttf")
467
468    if Font.title_font is None:
469        Font.title_font = Font(
470            liberation_sans_bold_path, int(0.04 * Render.get().display_height)
471        )
472    Font.title_font.set_size(int(0.04 * Render.get().display_height))
473    if Font.subtitle_font is None:
474        Font.subtitle_font = Font(
475            liberation_sans_bold_path, int(0.025 * Render.get().display_height)
476        )
477    Font.subtitle_font.set_size(int(0.025 * Render.get().display_height))
478    # if Font.small_font is None:
479    #     Font.small_font = Font(
480    #         liberation_sans_narrow_bold_path,
481    #         int(0.025 * Render.get().display_height))
482    # Font.small_font.set_size(int(0.025 * Render.get().display_height))
483    if Font.main_path_font is None:
484        Font.main_path_font = Font(
485            liberation_sans_bold_path, int(0.025 * Render.get().display_height)
486        )
487    Font.main_path_font.set_size(int(0.025 * Render.get().display_height))
488    if Font.list_subtitle_font is None:
489        Font.list_subtitle_font = Font(
490            liberation_sans_bold_path, int(0.020 * Render.get().display_height)
491        )
492    Font.list_subtitle_font.set_size(int(0.020 * Render.get().display_height))
493    # if Font.header_font is None:
494    #     Font.header_font = Font(
495    #         vera_font_path, int(0.06 * Render.get().display_height))
496    # Font.header_font.set_size(int(0.06 * Render.get().display_height))
497
498    if NotificationRender.font is None:
499        NotificationRender.font = Font(
500            liberation_sans_bold_path, int(0.020 * Render.get().display_height)
501        )
502
503
504char_buffer = ""
505char_buffer_last_updated = 0
506
507
508def handle_search_menu_on_character_press(char_buffer):
509    # if len(char_buffer) > 2 and t - char_buffer_last_updated > 0.5:
510    if len(char_buffer) > 2:
511        print(char_buffer)
512        create_search_results_menu(char_buffer)
513    if len(char_buffer) <= 2:
514        if isinstance(current_menu, SearchResultsMenu):
515            # collapse search menu
516
517            # if hasattr(current_menu, "search_text"):
518            print("setting current_menu (something with search)")
519            # set_current_menu(current_menu.parent_menu)
520            go_back()
521        else:
522            current_menu.search_text = char_buffer
523
524
525# noinspection PyUnusedLocal
526def character_press(char):
527    print("character press", repr(char))
528    if State.get().current_game:  # or isinstance(current_menu, GameMenu):
529        print("ignoring key press", repr(char))
530        return
531
532    # global char_buffer
533    char_buffer = current_menu.search_text
534
535    global char_buffer_last_updated
536    char_buffer_last_updated = State.get().time
537    # return
538
539    Render.get().dirty = True
540    if char == "RETURN":
541        print(char_buffer, len(char_buffer))
542        print("returning false")
543        return False
544    elif char == "BACKSPACE" or char == u"\b":
545        if ArcadeSettings().search():
546            char_buffer = char_buffer[:-1]
547            if len(char_buffer) <= 2:
548                char_buffer = ""
549            handle_search_menu_on_character_press(char_buffer)
550        return True
551    elif len(char) == 1:
552        if ArcadeSettings().search():
553            char_buffer += char
554            handle_search_menu_on_character_press(char_buffer)
555            if 1 <= len(char_buffer) <= 2:
556                jump_to_item = -1
557                for i, item in enumerate(current_menu.items):
558                    # FIXME: a bit hack-y this, should check sort_name
559                    # instead (need to store this in items then)
560                    # also, should use binary search and not sequential search
561                    searches = [
562                        char_buffer,
563                        "the " + char_buffer,
564                        "a " + char_buffer,
565                    ]
566                    check = item.name.lower()
567                    for s in searches:
568                        if check.startswith(s):
569                            jump_to_item = i
570                            break
571                if jump_to_item >= 0:
572                    current_menu.set_selected_index(
573                        jump_to_item, immediate=True
574                    )
575        return True
576    else:
577        raise Exception(repr(char))
578
579
580class SearchTextItem(MenuItem):
581    def __init__(self, title):
582        MenuItem.__init__(self)
583        self.path_title = title
584
585
586class SearchResultsMenu(ItemMenu):
587    pass
588
589
590def create_search_results_menu(text):
591    global current_menu
592    try:
593        if text == current_menu.search_text:
594            return False
595    except AttributeError:
596        pass
597    new_menu = SearchResultsMenu("Search Results")
598    new_menu.search_text = text
599    # words = [v.strip() for v in text.lower().split(" ")]
600    # print "Creating search results for", words
601    new_menu.top.append_left(SearchTextItem("Search: {0}_".format(text)))
602    new_menu.top.set_selected_index(
603        len(new_menu.top.left) + len(new_menu.top.right) - 1
604    )
605
606    # clause = []
607    # args = []
608    # for word in words:
609    #     clause.append("AND name like ?")
610    #     args.append("%{0}%".format(word))
611    # clause = " ".join(clause)
612    terms = GameNameUtil.extract_search_terms(text.lower())
613    for item in MenuItem.create_game_items(terms):
614        new_menu.append(item)
615    if len(new_menu) == 0:
616        new_menu.append(NoItem("No Search Results"))
617    # if hasattr(current_menu, "search_text"):
618    #     # replace current search menu, not append to path
619    #     #new_menu.parent_menu = current_menu.parent_menu
620    #     replace = True
621    # else:
622    #     #new_menu.parent_menu = current_menu
623    #     replace = False
624    replace = isinstance(current_menu, SearchResultsMenu)
625    print("create search results menu")
626    # set_current_menu(new_menu)
627    enter_menu(new_menu, replace=replace)
628    return True
629
630
631def rescan_games():
632    # global current_menu
633    print("rescan games -- currently disabled")
634    # GameScanner.scan()
635    # Render.get().dirty = True
636    pass
637
638
639def go_back():
640    c_menu = State.get().history[-1]
641    if hasattr(c_menu, "go_back"):
642        c_menu.go_back()
643        return
644    State.get().history.pop()
645    set_current_menu(State.get().history[-1])
646
647
648def default_input_func(button):
649    if button == "LEFT":
650        State.get().navigatable.go_left()
651    elif button == "RIGHT":
652        State.get().navigatable.go_right()
653    elif button == "UP":
654        State.get().navigatable.go_up()
655    elif button == "DOWN":
656        State.get().navigatable.go_down()
657    elif button == "SKIP_LEFT":
658        State.get().navigatable.go_left(10)
659    elif button == "SKIP_RIGHT":
660        State.get().navigatable.go_right(10)
661    elif button == "PRIMARY":
662
663        # global char_buffer
664        # char_buffer = ""
665
666        State.get().navigatable.activate()
667
668    elif button == "BACK":
669        print("-- button is BACK -- ")
670        if State.get().config_menu:
671            State.get().config_menu = None
672            set_current_menu(State.get().current_menu)
673            set_items_brightness(0.66, duration=0.500)
674        elif State.get().current_game is None and current_menu.search_text:
675            InputHandler.get_button()  # clear OK status
676            character_press("BACKSPACE")
677        elif State.get().current_game:
678            State.get().current_game = None
679            # game_fade_animation = AnimateValueBezier(
680            #     (MenuGameTransition, "value"),
681            #     1.0, State.get().time,
682            #     1.0, State.get().time + 0.133,
683            #     0.0, State.get().time + 0.133,
684            #     0.0, State.get().time + 0.400)
685
686        # elif can_navigate and current_menu.parent_menu:
687        # elif can_navigate and len(State.get().history) > 1:
688        elif len(State.get().history) > 1:
689            go_back()
690    elif button == "QUIT":
691        State.get().quit = True
692
693
694def render_top():
695    Render.get().hd_perspective()
696    gl.glPushMatrix()
697    transition = State.get().current_menu.top_menu_transition
698    gl.glTranslate(0.0, (1.0 - transition) * 90, 0.9)
699
700    gl.glTranslate(0.0, 0.0, 0.05)
701
702    if State.get().top_menu == State.get().navigatable:
703        selected_index = State.get().top_menu.get_selected_index()
704    else:
705        selected_index = -1
706    x = State.get().gl_left
707    for item in State.get().top_menu.left:
708        item.update_size_left()
709        item.x = x
710        item.y = 1080 - TOP_HEIGHT
711        item.h = TOP_HEIGHT
712        x += item.w
713
714    index = len(State.get().top_menu.left) - 1
715    for item in reversed(State.get().top_menu.left):
716        item.render_top_left(selected=(index == selected_index))
717        index -= 1
718
719    index = len(State.get().top_menu) - 1
720    x = State.get().gl_right
721    for item in reversed(State.get().top_menu.right):
722        item.update_size_right()
723        x -= item.w
724        item.x = x
725        item.y = 1080 - TOP_HEIGHT
726        item.h = TOP_HEIGHT
727        if Mouse.focus:
728            selected = Mouse.focus == item
729        else:
730            selected = index == selected_index
731        item.render_top_right(selected=selected)
732        Mouse.items.append(item)
733        index -= 1
734    gl.glPopMatrix()
735
736
737def render_config_menu():
738    if not State.get().config_menu:
739        return
740
741    Render.get().ortho_perspective()
742    config_menu = State.get().config_menu
743    # text = config_menu.items[config_menu.index]
744    # otw, th = Render.get().text(text, title_font,
745    #         -1.0, -0.93, 2.0, color=(1.0, 1.0, 1.0, 0.36 * strength))
746    # x = 0.0 + otw / 2 + CONFIG_SEPARATION
747    # for i in range(config_menu.index + 1, len(config_menu.items)):
748    #     text = config_menu.items[i]
749    #     tw, th = Render.get().text(text, title_font,
750    #             x, -0.93, color=(1.0, 1.0, 1.0, 0.36 * strength))
751    #     x += tw + CONFIG_SEPARATION
752    # x = 0.0 - otw / 2 - CONFIG_SEPARATION
753    x = -0.55
754    y = 0.8
755    for i in range(len(config_menu.items)):
756        text = config_menu.items[i].upper()
757        # tw, th = Render.get().measure_text(text, title_font)
758        # x -= tw + CONFIG_SEPARATION
759        y -= 0.15
760        if i == config_menu.index and config_menu == State.get().navigatable:
761            color = (1.0, 1.0, 1.0, 1.0)
762        else:
763            color = (1.0, 1.0, 1.0, 0.33)
764        Render.get().text(text, Font.subtitle_font, x, y, color=color)
765
766
767def render_scanning_status():
768    # Render.get().hd_perspective()
769    # text = GameScanner.get_status()
770    # Render.get().dirty = True
771    #
772    # fs_emu_texturing(False)
773    #
774    # z = 0.0
775    #
776    # glBegin(GL_QUADS)
777    # glColor3f(0.0, 0.0, 0.0)
778    # glVertex3f(   0, 500, z)
779    # glVertex3f(1920, 500, z)
780    # glVertex3f(1920, 700, z)
781    # glVertex3f(   0, 700, z)
782    # glEnd()
783    #
784    # Render.get().text(text, Font.title_font, 200, 600, color=(1.0, 1.0,
785    # 1.0, 1.0))
786    #
787    # glBegin(GL_QUADS)
788    # glColor3f(1.0, 1.0, 1.0)
789    # x = 200
790    # y = 500
791    # z = 0.9
792    # x2 = 200 + 1520 * GameScanner.progress
793    # glVertex3f(x, y, z)
794    # glVertex3f(x2, y, z)
795    # glVertex3f(x2, y + 20, z)
796    # glVertex3f(x, y + 20, z)
797    # glEnd()
798    # fs_emu_texturing(True)
799    pass
800
801
802def do_render():
803    if current_menu is None:
804        return
805
806    current_menu.update()
807
808    if RunTransition.value > 0.99:
809        # do not render anything when running a game
810        return
811
812    if State.get().currently_ingame:
813        # print("currently ingame")
814        return
815
816    # try to exploit parallelism by uploading texture while rendering
817    TextureManager.get().load_textures(1)
818
819    # clear mouseover rects -these will be calculated during rendering
820    Mouse.items[:] = []
821    Render.get().standard_perspective()
822
823    # scanning = GameScanner.scanning
824
825    data = current_menu.render()
826    current_menu.render_transparent(data)
827
828    # if GameScanner.is_scanning():
829    if False:
830        render_scanning_status()
831        State.get().was_scanning = True
832    else:
833        render_config_menu()
834        if State.get().was_scanning:
835            print("State.get().was_scanning")
836            State.get().was_scanning = False
837            # reload current menu
838
839            # if current_menu.parent_menu:
840            #     result = current_menu.parent_menu.selected_item.activate(
841            #             current_menu.parent_menu)
842            #     if isinstance(result, Menu):
843            #         #if len(result) == 0:
844            #         #    result.append(NoItem())
845            #         result.parent_menu = current_menu.parent_menu
846            #         print("set new menu (rescanned games)")
847            #         set_current_menu(result)
848            recreate_main_menu_if_necessary()
849
850    render_top()
851
852    if State.get().dialog:
853        State.get().dialog.render()
854
855    render_global_fade()
856    NotificationRender.render()
857    if RunTransition.value > 0.0:
858        render_fade(a=RunTransition.value)
859
860
861def render_fade(r=0.0, g=0.0, b=0.0, a=0.0):
862    Render.get().hd_perspective()
863    fs_emu_blending(True)
864    fs_emu_texturing(False)
865    gl.glBegin(gl.GL_QUADS)
866    gl.glColor4f(r * a, g * a, b * a, a)
867    gl.glVertex2f(0, 0)
868    gl.glVertex2f(1920, 0)
869    gl.glVertex2f(1920, 1080)
870    gl.glVertex2f(0, 1080)
871    gl.glEnd()
872
873
874def render_global_fade():
875    t = State.get().time
876    if State.get().fade_end >= t >= State.get().fade_start:
877        a = (t - State.get().fade_start) / (
878            State.get().fade_end - State.get().fade_start
879        )
880        if a < 0.0:
881            a = 0.0
882        elif a > 1.0:
883            a = 1.0
884
885        Render.get().hd_perspective()
886        gl.glPushMatrix()
887        gl.glTranslatef(0.0, 0.0, 0.99999)
888        fs_emu_blending(True)
889        fs_emu_texturing(True)
890        if State.get().fade_splash:
891            Texture.splash.render(
892                (1920 - Texture.splash.w) // 2,
893                (1080 - Texture.splash.h) // 2,
894                Texture.splash.w,
895                Texture.splash.h,
896                opacity=(1.0 - a),
897            )
898
899        c = [0, 0, 0, (1.0 - a)]
900        # for i in range(4):
901        #     c[i] = State.get().fade_from[i] + \
902        #             (State.get().fade_to[i] - State.get().fade_from[i]) * a
903        #  * (a)
904        render_fade(*c)
905        gl.glPopMatrix()
906        Render.get().dirty = True
907
908
909FPS_FRAMES = 100
910render_times = deque()
911for _ in range(FPS_FRAMES):
912    render_times.append(0)
913
914fps_str = ""
915debug_x = 0
916debug_x_2 = 0
917
918
919def render_debug_square():
920    global debug_x
921    Render.get().hd_perspective()
922    fs_emu_texturing(False)
923    gl.glBegin(gl.GL_QUADS)
924    gl.glColor3f(1.0, 1.0, 1.0)
925    x = debug_x
926    debug_x += 1
927    if debug_x >= 1920:
928        debug_x = 0
929    y = 989
930    z = 0.99
931    gl.glVertex3f(x, y, z)
932    gl.glVertex3f(x + 20, y, z)
933    gl.glVertex3f(x + 20, y + 20, z)
934    gl.glVertex3f(x, y + 20, z)
935    gl.glEnd()
936    fs_emu_texturing(True)
937
938
939def render_debug_square_2():
940    global debug_x_2
941    Render.get().hd_perspective()
942    fs_emu_texturing(False)
943    gl.glBegin(gl.GL_QUADS)
944    gl.glColor3f(0.2, 0.2, 0.2)
945    x = debug_x_2
946    debug_x_2 += 1
947    if debug_x_2 >= 1920:
948        debug_x_2 = 0
949    y = 989 + 5
950    z = 0.99
951    gl.glVertex3f(x, y, z)
952    gl.glVertex3f(x + 20, y, z)
953    gl.glVertex3f(x + 20, y + 10, z)
954    gl.glVertex3f(x, y + 10, z)
955    gl.glEnd()
956    fs_emu_texturing(True)
957
958
959# FIXME: remove / move some code away
960def swap_buffers():
961    # global fps_str
962    #
963    # Render.get().ortho_perspective()
964    # #glPushMatrix()
965    # #glTranslatef(0.0, 0.0, 0.5)
966    #
967    # #if not fps_str:
968    # #    fps_str = "WHY?"
969    #
970    # if Render.get().display_fps or True:
971    #     if fps_str:
972    #         Render.get().text(fps_str, Font.main_path_font,
973    #                     -1.74, 0.82, h=0.1, color=(0.25, 0.25, 0.25, 1.0))
974    # #glPopMatrix()
975    #
976    # # FIXME: Why does not minimize from fullscreen work on ATI unless we
977    # render
978    # # something here?
979    # glBindTexture(GL_TEXTURE_2D, 0)
980    # glPushMatrix()
981    # glTranslate(2000.0, 0.0, 0.0)
982    # glBegin(GL_QUADS)
983    # glVertex2f(0.0, 0.0)
984    # glVertex2f(1.0, 0.0)
985    # glVertex2f(1.0, 1.0)
986    # glVertex2f(0.0, 1.0)
987    # glEnd()
988    # glPopMatrix()
989    #
990    # #fs_emu_blending(False)
991    # #glEnable(GL_DEPTH_TEST)
992    #
993    # #if Render.get().display_sync:
994    # #    glFinish()
995    #
996    # #pygame.display.flip()
997    # print("FIXME: not flipping")
998    #
999    # if Render.get().display_sync:
1000    #     # give up time slice
1001    #     time.sleep(0.001)
1002    #     glFinish()
1003    #
1004    # if Render.get().display_fps:
1005    #     t = get_current_time()
1006    #     render_times.append(t)
1007    #     t0 = render_times.popleft()
1008    #     if t0 > 0 and State.get().frame_number % 5 == 0:
1009    #         time_diff = t - t0
1010    #         #print("{0:0.2f}".format(300.0 / time_diff))
1011    #         fps = FPS_FRAMES / time_diff
1012    #         if fps >= 100:
1013    #             fps_str = "FPS:  {0:0.0f}".format(fps)
1014    #         else:
1015    #             fps_str = "FPS: {0:0.1f}".format(fps)
1016    #         #Render.get().text(fps_str, Font.title_font,
1017    #         #        -1.0, 0.90, 2.0, shadow=True)
1018    State.get().frame_number += 1
1019    Render.get().delete_textures()
1020
1021
1022def render_screen():
1023    # set Render.get().dirty to False here, so that render functions can
1024    # request a new render frame by setting dirty
1025    Render.get().dirty = False
1026    # glEnable(GL_SCISSOR_TEST)
1027    # can_idle =
1028    if SDL_IsMinimized():
1029        time.sleep(0.01)
1030        return
1031    # Render.get().dirty = True
1032    gl.glClearColor(0.0, 0.0, 0.0, 1.0)
1033    gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
1034    do_render()
1035    if Render.get().display_fps:
1036        Render.get().dirty = True
1037    if RENDER_DEBUG_SQUARES:
1038        render_debug_square()
1039
1040
1041# noinspection PyPep8Naming
1042def init_display():
1043    global display
1044    global real_display_height  # , display_yoffset
1045
1046    # global banner_texture, shadow_texture, gloss_texture
1047    # global top_texture, top_logo_texture, logo_texture
1048    # global missing_cover_texture, default_item_texture
1049    # global backdrop_texture
1050
1051    logger.debug("Init OpenGL menu display")
1052
1053    DisplayLists.clear()
1054
1055    # on_resize()
1056    # depth = 0
1057    # FIXME: HACK / TESTING
1058    # if not Settings.fullscreen_menu:
1059    #    if windows:
1060    #        os.environ["SDL_VIDEO_WINDOW_POS"] = "3,29"
1061    #    else:
1062    #        os.environ["SDL_VIDEO_WINDOW_POS"] = "0,0"
1063    # maximize_window = (not Settings.fullscreen_menu and
1064    #                    Settings.windowed_size is None)
1065
1066    # display_info = pygame.display.Info()
1067    # dw = display_info.current_w
1068    # dh = display_info.current_h
1069    # dw, dh = fsui.get_screen_size()
1070    # dw, dh = 100, 100
1071    Render.get().display_width = main_window.width
1072    Render.get().display_height = main_window.height
1073
1074    # if Settings.fullscreen_menu:
1075    #     print("fullscreen is True")
1076    #     if windows:
1077    #         #resolution = (0, 0)
1078    #         #flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.NOFRAME \
1079    #         #        | pygame.FULLSCREEN
1080    #         os.environ["SDL_VIDEO_WINDOW_POS"] = "0,0"
1081    #         flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.NOFRAME
1082    #         #flags = flags | pygame.FULLSCREEN
1083    #         #if fs.linux:
1084    #         #    pass
1085    #         #else:
1086    #         if dw > dh * 2:
1087    #             # Assume dual monitor setup - hack for Linux / SDL
1088    #             resolution = (dw / 2, dh)
1089    #         else:
1090    #             resolution = (dw, dh)
1091    #     else:  # fullscreen, but not microsoft windows
1092    #         #resolution = (0, 0)
1093    #         flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.NOFRAME #|
1094    # pygame.FULLSCREEN
1095    #         if dw > dh * 2:
1096    #             # Assume dual monitor setup - hack for Linux / SDL
1097    #             resolution = (dw / 2, dh)
1098    #         else:
1099    #             resolution = (dw, dh)
1100    #         if linux:
1101    #             overscan = Config.get("display/overscan", "0,0,0,0")
1102    #             try:
1103    #                 overscan = overscan.split(",")
1104    #                 overscan = [int(x.strip()) for x in overscan]
1105    #                 print("using overscan", overscan)
1106    #             except Exception as e:
1107    #                 print("error parsing overscan from config:", repr(e))
1108    #                 overscan = [0, 0, 0, 0]
1109    #             os.environ["SDL_VIDEO_WINDOW_POS"] = "{0},{1}".format(
1110    #                 overscan[0], overscan[1])
1111    #             resolution = (resolution[0] - overscan[0] - overscan[2],
1112    #                           resolution[1] - overscan[1] - overscan[3])
1113    #         elif macosx:
1114    #             # FIXME: fullscreen mode does not work very well. -When
1115    #             # opening a fullscreen emulator from fullscreen, the emulator
1116    #             # crashes on glViewport. Tested with fs-amiga.
1117    #             #flags |= pygame.FULLSCREEN
1118    #
1119    #             # for now, we create an almost maximized window, works
1120    #             # quite well when the dock is set to auto-hide
1121    #             #resolution = (resolution[0], resolution[1] - 22)
1122    #
1123    #             # FIXME: trying LSUIPresentationMode
1124    #             os.environ["SDL_VIDEO_WINDOW_POS"] = "0,0"
1125    #
1126    #             # kUIModeNormal = 0
1127    #             # kUIModeContentSuppressed = 1
1128    #             # kUIModeContentHidden = 2
1129    #             # kUIModeAllSuppressed = 4
1130    #             kUIModeAllHidden = 3
1131    #             kUIOptionAutoShowMenuBar = 1 << 0
1132    #             # kUIOptionDisableAppleMenu = 1 << 2
1133    #             # kUIOptionDisableProcessSwitch = 1 << 3
1134    #             # kUIOptionDisableForceQuit = 1 << 4
1135    #             # kUIOptionDisableSessionTerminate = 1 << 5
1136    #             # kUIOptionDisableHide = 1 << 6
1137    #
1138    #             #noinspection PyUnresolvedReferences
1139    #             import objc
1140    #             #noinspection PyUnresolvedReferences
1141    #             from Foundation import NSBundle
1142    #             bundle = NSBundle.bundleWithPath_(
1143    #                 "/System/Library/Frameworks/Carbon.framework")
1144    #             objc.loadBundleFunctions(
1145    #                 bundle, globals(),
1146    #                 ((str("SetSystemUIMode"), str("III"), str("")),))
1147    #             #noinspection PyUnresolvedReferences
1148    #             SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar)
1149    #
1150    # else:
1151    #     if windows and maximize_window and \
1152    #             not Settings.window_decorations:
1153    #         import ctypes
1154    #         SPI_GETWORKAREA = 48
1155    #
1156    #         class RECT(ctypes.Structure):
1157    #             _fields_ = [
1158    #                 ("left", ctypes.c_ulong),
1159    #                 ("top", ctypes.c_ulong),
1160    #                 ("right", ctypes.c_ulong),
1161    #                 ("bottom", ctypes.c_ulong)]
1162    #
1163    #         m = ctypes.windll.user32
1164    #         r = RECT()
1165    #         m.SystemParametersInfoA(SPI_GETWORKAREA, 0, ctypes.byref(r), 0)
1166    #         x = int(r.left)
1167    #         y = int(r.top)
1168    #         w = int(r.right) - int(r.left)
1169    #         h = int(r.bottom) - int(r.top)
1170    #         print(x, y, w, h)
1171    #         WINDOWED_SIZE[0] = w
1172    #         WINDOWED_SIZE[1] = h
1173    #         os.environ["SDL_VIDEO_WINDOW_POS"] = "{0},{1}".format(x, y)
1174    #         State.get().allow_minimize = False
1175    #
1176    #     if Settings.windowed_size:
1177    #         print("Settings.windowed_size", Settings.windowed_size)
1178    #         WINDOWED_SIZE[0] = Settings.windowed_size[0]
1179    #         WINDOWED_SIZE[1] = Settings.windowed_size[1]
1180    #         Render.get().display_width = WINDOWED_SIZE[0]
1181    #         Render.get().display_height = WINDOWED_SIZE[1]
1182    #         #if dw > 1400:
1183    #         #    Render.get().display_width = 1280
1184    #         #    Render.get().display_height = 720
1185    #     else:
1186    #         Render.get().display_width = WINDOWED_SIZE[0]
1187    #         Render.get().display_height = WINDOWED_SIZE[1]
1188    #     resolution = (Render.get().display_width, Render.get().display_height)
1189    #     #print(resolution)
1190    #     #sys.exit(1)
1191    #     if Settings.window_decorations:
1192    #         flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.RESIZABLE
1193    #     else:
1194    #         flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.NOFRAME
1195    #
1196    # display_yoffset = 0
1197    # Mouse.set_visible(False)
1198    #
1199    # pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 8)
1200    # pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 16)
1201    #
1202    # Render.get().display_sync = ENABLE_VSYNC
1203    # if Render.get().display_sync:
1204    #     print("enabling vertical sync")
1205    #     os.environ["__GL_SYNC_TO_VBLANK"] = "1"
1206    #     pygame.display.gl_set_attribute(pygame.GL_SWAP_CONTROL, 1)
1207    # else:
1208    #     os.environ["__GL_SYNC_TO_VBLANK"] = "0"
1209    #     pygame.display.gl_set_attribute(pygame.GL_SWAP_CONTROL, 0)
1210    # pygame.display.gl_set_attribute(pygame.GL_DOUBLEBUFFER, 1)
1211    # fsaa = Config.get_int("video/fsaa", 0)
1212    # if fsaa:
1213    #     pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1)
1214    #     pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, fsaa)
1215    # print("pygame set display mode", resolution, flags, depth)
1216    # display = pygame.display.set_mode(resolution, flags, depth)
1217    # if not Settings.fullscreen_game:
1218    #     try:
1219    #         del os.environ["SDL_VIDEO_WINDOW_POS"]
1220    #     except KeyError:
1221    #         pass
1222    #
1223    # if app.name == "fs-uae-arcade":
1224    #     pygame.display.set_caption("FS-UAE Arcade")
1225    # else:
1226    #     pygame.display.set_caption("FS Game Center")
1227    #
1228    # # FIXME: DISABLING MAXIMIZE FOR DEBUGGING
1229    # #maximize_window = False
1230    # if maximize_window:
1231    #     print("maximizing window")
1232    #     SDL_Maximize()
1233    #     for event in pygame.event.get():
1234    #         if event.type == pygame.VIDEORESIZE:
1235    #             #WINDOWED_SIZE[0] = event.w
1236    #             #WINDOWED_SIZE[1] = event.h
1237    #             on_resize((event.w, event.h))
1238    #     print("DISPLAY.GET_SIZE", display.get_size())
1239    # else:
1240    #     on_resize(display.get_size())
1241
1242    gl.glMatrixMode(gl.GL_MODELVIEW)
1243
1244    gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
1245    gl.glClearColor(*State.get().fade_from)
1246
1247    fs_emu_texturing(True)
1248    Texture.splash = Texture("splash.png")
1249
1250    for _ in range(0):
1251        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
1252        # gl.glClear(gl.GL_COLOR_BUFFER_BIT)
1253        Render.get().hd_perspective()
1254        Texture.splash.render(
1255            (1920 - Texture.splash.w) // 2,
1256            (1080 - Texture.splash.h) // 2,
1257            Texture.splash.w,
1258            Texture.splash.h,
1259        )
1260        gl.glFinish()
1261        # pygame.display.flip()
1262        gl.glFinish()
1263
1264    gl.glEnable(gl.GL_DEPTH_TEST)
1265
1266
1267def init_textures():
1268    Texture.shadow = Texture.from_resource("shadow.png")
1269    Texture.shadow2 = Texture.from_resource("shadow2.png")
1270    # Texture.gloss = Texture.from_resource("gloss.png")
1271    Texture.gloss = Texture("gloss.png")
1272    Texture.screen_gloss = Texture("screen_gloss.png")
1273    Texture.static = Texture("preview_static0.png")
1274    Texture.default_item = Texture("default_item.png")
1275    Texture.missing_screenshot = Texture("missing_screenshot.png")
1276    Texture.missing_cover = Texture("missing_cover.png")
1277    # path = os.path.join(fs.get_data_dir(), "logo.png")
1278    # if os.path.exists(path):
1279    #     im = Image.open(path)
1280    #     Texture.logo = Texture.load(im)
1281    # else:
1282    Texture.logo = Texture.from_resource("logo.png")
1283    Texture.top = Texture.from_resource("top.png")
1284    Texture.top_logo = Texture("top_logo.png")
1285    Texture.top_logo_selected = Texture("top_logo_selected.png")
1286
1287    Texture.add = Texture("add.png")
1288    Texture.add_selected = Texture("add_selected.png")
1289    Texture.home = Texture("home.png")
1290    Texture.home_selected = Texture("home_selected.png")
1291    Texture.minimize = Texture("minimize.png")
1292    Texture.minimize_selected = Texture("minimize_selected.png")
1293    Texture.close = Texture("close.png")
1294    Texture.close_selected = Texture("close_selected.png")
1295    Texture.shutdown = Texture("shutdown.png")
1296    Texture.shutdown_selected = Texture("shutdown_selected.png")
1297
1298    Texture.bottom_bar = Texture("bottom_bar.png")
1299    Texture.screen_border_1 = Texture("screen_border_1.png")
1300    Texture.screen_border_2 = Texture("screen_border_2.png")
1301    Texture.top_background = Texture("top_background.png")
1302    Texture.top_item = Texture("top_item.png")
1303    Texture.top_item_selected = Texture("top_item_selected.png")
1304    Texture.top_item_left = Texture("top_item_left.png")
1305    Texture.top_item_left_selected = Texture("top_item_left_selected.png")
1306    Texture.top_item_right = Texture("top_item_right.png")
1307    Texture.top_item_arrow = Texture("top_item_arrow.png")
1308    Texture.top_item_arrow_selected = Texture("top_item_arrow_selected.png")
1309
1310    Texture.sidebar_background = Texture("sidebar_background.png")
1311    Texture.sidebar_background_shadow = Texture(
1312        "sidebar_background_shadow.png"
1313    )
1314    Texture.glow_top = Texture("glow_top.png")
1315    Texture.glow_top_left = Texture("glow_top_left.png")
1316    Texture.glow_left = Texture("glow_left.png")
1317
1318    Texture.heading_strip = Texture("heading_strip.png")
1319    Texture.item_background = Texture("item_background.png")
1320    Texture.top_item_background = Texture("top_item_background.png")
1321    Texture.logo_32 = Texture("logo-32.png")
1322
1323    Texture.stretch = Texture("stretch.png")
1324    Texture.aspect = Texture("stretch-aspect.png")
1325    Texture.square_pixels = Texture("stretch-none.png")
1326
1327    # # FIXME: TEMPORARY - FOR TESTING, ONLY
1328    # path = "c:\\git\\fs-game-database\\Backdrops\\ffx.png"
1329    # if os.path.exists(path):
1330    #     im = Image.open(path)
1331    #     #im = resources.get_resource_image_pil("shadow.png")
1332    #     assert im.size == (1024, 1024)
1333    #     imdata = im.tostring("raw", "RGB")
1334    #     backdrop_texture = Render.get().create_texture()
1335    #     glBindTexture(GL_TEXTURE_2D, backdrop_texture)
1336    #     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, im.size[0], im.size[1], 0,
1337    #                  GL_RGB, GL_UNSIGNED_BYTE, imdata)
1338    #     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
1339    #     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
1340    # else:
1341    #     backdrop_texture = 0
1342
1343
1344def init_lighting():
1345    gl.glLightModeli(gl.GL_LIGHT_MODEL_LOCAL_VIEWER, gl.GL_TRUE)
1346    gl.glLightModeli(gl.GL_LIGHT_MODEL_TWO_SIDE, gl.GL_FALSE)
1347    gl.glLightModeli(
1348        gl.GL_LIGHT_MODEL_COLOR_CONTROL, gl.GL_SEPARATE_SPECULAR_COLOR
1349    )
1350
1351    light_position = (0.0, 0.0, 3.0, 1.0)
1352    gl.glLightfv(gl.GL_LIGHT0, gl.GL_POSITION, light_position)
1353    gl.glLightfv(gl.GL_LIGHT0, gl.GL_DIFFUSE, (1.0, 1.0, 1.0, 1.0))
1354    gl.glLightfv(gl.GL_LIGHT0, gl.GL_SPECULAR, (0.0, 0.0, 0.0, 1.0))
1355    gl.glEnable(gl.GL_LIGHT0)
1356
1357    gl.glLightfv(gl.GL_LIGHT1, gl.GL_DIFFUSE, (0.0, 0.0, 0.0, 1.0))
1358    gl.glLightfv(gl.GL_LIGHT1, gl.GL_SPECULAR, (1.0, 1.0, 1.0, 1.0))
1359    gl.glEnable(gl.GL_LIGHT1)
1360
1361    gl.glLightfv(gl.GL_LIGHT2, gl.GL_DIFFUSE, (0.0, 0.0, 0.0, 1.0))
1362    gl.glLightfv(gl.GL_LIGHT2, gl.GL_SPECULAR, (0.5, 0.5, 0.5, 1.0))
1363    gl.glEnable(gl.GL_LIGHT2)
1364
1365    gl.glMaterialfv(gl.GL_FRONT, gl.GL_AMBIENT, (0.1, 0.1, 0.1, 1.0))
1366    gl.glMaterialfv(gl.GL_FRONT, gl.GL_SHININESS, (10,))
1367
1368
1369def handle_videoresize_event(event):
1370    WINDOWED_SIZE[0] = event.w
1371    WINDOWED_SIZE[1] = event.h
1372    on_resize((event.w, event.h))
1373
1374
1375def on_resize(display_size):
1376    print("glui.window.on_resize", display_size)
1377    # traceback.print_stack()
1378
1379    global display
1380    global real_display_height, display_yoffset
1381    global browse_curve, header_curve  # ,screenshot_curve
1382
1383    DisplayLists.clear()
1384
1385    Render.get().display_width, Render.get().display_height = display_size  #
1386    #  display# .get_size()
1387    State.get().display_aspect = (
1388        Render.get().display_width / Render.get().display_height
1389    )
1390    print(WINDOWED_SIZE)
1391    Settings.windowed_size = tuple(WINDOWED_SIZE)
1392
1393    real_display_height = Render.get().display_height
1394    # if Render.get().display_width / Render.get().display_height < 4 / 3:
1395    #    Render.get().display_height = int(Render.get().display_width / 4 * 3)
1396    #    display_yoffset = (real_display_height - Render.get(
1397    # ).display_height) // 2
1398    # else:
1399    display_yoffset = 0
1400    # glViewport(0, 0, Render.get().display_width, real_display_height)
1401    # glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
1402    # swap_buffers()
1403    # glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
1404    # swap_buffers()
1405    print(display_yoffset)
1406
1407    reinit_fonts()
1408
1409    print(
1410        0,
1411        display_yoffset,
1412        Render.get().display_width,
1413        Render.get().display_height,
1414    )
1415    gl.glViewport(
1416        0,
1417        display_yoffset,
1418        Render.get().display_width,
1419        Render.get().display_height,
1420    )
1421
1422    # aspect_ratio = max(4 / 3, (Render.get().display_width / Render.get(
1423    # ).display_height))
1424    factor = (Render.get().display_width / Render.get().display_height) / (
1425        1024 / 600
1426    )
1427    browse_curve = Bezier.bezier(
1428        (-5.0 * factor, -10.0),
1429        (-1.7 * factor, -0.0),
1430        (1.7 * factor, -0.0),
1431        (5.0 * factor, -10.0),
1432    )
1433    header_curve = Bezier.bezier(
1434        (-2.0 * factor, 0.00),
1435        (-1.0 * factor, 0.075),
1436        (1.0 * factor, 0.075),
1437        (2.0 * factor, 0.00),
1438        steps=50,
1439    )
1440
1441    # if USE_MENU_TRANSITIONS:
1442    #    State.get().fbo = FrameBufferObject(
1443    #        Render.get().display_width, Render.get().display_height)
1444    Render.get().dirty = True
1445
1446
1447class LogoStrength(object):
1448    anim = None
1449    value = 1.0
1450
1451
1452def set_ingame_status():
1453    # State.get().fade_start = State.get().time
1454    # State.get().fade_end = State.get().time + 86400.0 * 365.0
1455    # State.get().fade_from = (0.0, 0.0, 0.0, 0.0)
1456    # State.get().fade_to = (0.0, 0.0, 0.0, 0.0)
1457    # State.get().fade_splash = False
1458    print("State.get().currently_ingame = True")
1459    State.get().currently_ingame = True
1460
1461
1462def back_to_menu_from_game():
1463    print("State.get().currently_ingame = False")
1464    State.get().currently_ingame = False
1465
1466    set_items_brightness(0.66, duration=0.500)
1467
1468    RunTransition.value = 0.0
1469    RunTransition.anim = None
1470    LogoStrength.anim = None
1471    LogoStrength.value = 1.0
1472
1473    Render.get().zoom = 1.0
1474    Render.get().offset_x = 0.0
1475    Render.get().offset_y = 0.0
1476
1477    State.get().fade_splash = True
1478    State.get().fade_start = State.get().time
1479    State.get().fade_end = State.get().time + 0.5
1480    State.get().fade_from = (0.0, 0.0, 0.0, 2.0)
1481    State.get().fade_to = (0.0, 0.0, 0.0, 0.0)
1482
1483
1484def fade_quit():
1485    print("fade_quit")
1486    duration = 0.500
1487    alpha = 0.0
1488    start = get_current_time()
1489    while True:
1490        alpha = min(1.0, (get_current_time() - start) / duration)
1491
1492        def render_func():
1493            render_screen()
1494            Render.get().ortho_perspective()
1495            fs_emu_blending(True)
1496            fs_emu_texturing(False)
1497            gl.glDisable(gl.GL_DEPTH_TEST)
1498            gl.glBegin(gl.GL_QUADS)
1499            gl.glColor4f(0.0, 0.0, 0.0, alpha)
1500            gl.glVertex2f(-10.0, -1.0)
1501            gl.glVertex2f(10.0, -1.0)
1502            gl.glVertex2f(10.0, 1.0)
1503            gl.glVertex2f(-10.0, 1.0)
1504            gl.glEnd()
1505            gl.glEnable(gl.GL_DEPTH_TEST)
1506            # fs_emu_blending(False)
1507            fs_emu_texturing(True)
1508            swap_buffers()
1509            Render.get().dirty = True
1510
1511        Render.get().dirty = True
1512        main_loop_iteration(input_func=None, render_func=render_func)
1513        if alpha >= 1.0:
1514            break
1515
1516
1517def default_render_func():
1518    # rendered = False
1519
1520    time_str = time.strftime("%H:%M")
1521    if time_str != Render.get().last_time_str:
1522        # clock must be updated, at least
1523        Render.get().dirty = True
1524        Render.get().last_time_str = time_str
1525
1526    if Render.get().dirty or ALWAYS_RENDER:
1527        # print(Render.get().frame_number)
1528        render_screen()
1529        # rendered = True
1530        Render.get().twice = False
1531        # Render.get().twice = True
1532    else:
1533        if not Render.get().twice:
1534            Render.get().twice = True
1535            render_screen()
1536            # rendered = True
1537    if RENDER_DEBUG_SQUARES:
1538        render_debug_square_2()
1539    # if not rendered:
1540    #     pass
1541    # time.sleep(0.01)
1542    swap_buffers()
1543
1544
1545def find_item_at_coordinate(pos):
1546    menu = current_menu
1547    # Just checking top items for now
1548    for item in menu.top.left:
1549        if (
1550            item.x <= pos[0] <= item.x + item.w
1551            and item.y <= pos[1] <= item.y + item.h
1552        ):
1553            return item
1554    for item in menu.top.right:
1555        if (
1556            item.x <= pos[0] <= item.x + item.w
1557            and item.y <= pos[1] <= item.y + item.h
1558        ):
1559            return item
1560    return None
1561
1562
1563def handle_mouse_event(event):
1564    if event["type"] == "mouse-motion":
1565        item = find_item_at_coordinate(event["pos"])
1566        # print("mouse over item", item)
1567        state = State.get()
1568        state.mouse_item = item
1569    elif event["type"] == "mouse-press":
1570        item = find_item_at_coordinate(event["pos"])
1571        state = State.get()
1572        state.mouse_press_item = item
1573    elif event["type"] == "mouse-release":
1574        item = find_item_at_coordinate(event["pos"])
1575        state = State.get()
1576        # state.mouse_press_item = item
1577        if item is not None and item == state.mouse_press_item:
1578            item.activate(current_menu)
1579        state.mouse_press_item = None
1580
1581
1582def main_loop_iteration(
1583    input_func=default_input_func, render_func=default_render_func
1584):
1585    state = State.get()
1586
1587    # if State.get().currently_ingame:
1588    #     print("currently ingame")
1589    #     #return False
1590
1591    # print("main loop iteration")
1592    # time.sleep(0.1)
1593    # stop_loop = False
1594
1595    # if State.get().idle_from and State.get().idle_from < get_current_time():
1596    #     #print(State.get().idle_from)
1597    #     if not Render.get().dirty:
1598    #         #print("waiting for events...")
1599    #         events = [pygame.event.wait()]
1600    #     else:
1601    #         events = pygame.event.get()
1602    # else:
1603    #     events = pygame.event.get()
1604
1605    state.time = get_current_time()
1606
1607    # Main.process()
1608
1609    # t = State.get().time
1610
1611    # if len(char_buffer) > 2 and t - char_buffer_last_updated > 0.5:
1612    # print(current_menu, isinstance(current_menu, ItemMenu))
1613    # if isinstance(current_menu, ItemMenu):
1614    #
1615    #    # if len(char_buffer) > 2 and t - char_buffer_last_updated > 0.5:
1616    #    if len(char_buffer) > 2:
1617    #        create_search_results_menu(char_buffer)
1618    #    if len(char_buffer) <= 2:
1619    #        # collapse search menu
1620    #
1621    #        if hasattr(current_menu, "search_text"):
1622    #            print("setting current_menu (something with search)")
1623    #            set_current_menu(current_menu.parent_menu)
1624
1625    if state.hide_mouse_time and state.time > state.hide_mouse_time:
1626        if Mouse.focus:
1627            # keep cursor visible if mouse has focus
1628            pass
1629        else:
1630            state.hide_mouse_time = 0
1631            Mouse.set_visible(False)
1632
1633    had_mouse_event = False
1634    for event in InputHandler.pop_all_text_events():
1635        if event["type"] == "text":
1636            # if key was handled as a virtual button, only allow this
1637            # character if already started typing something, or else
1638            # navigating with X-Arcade may start searching for games
1639            if InputHandler.peek_button():
1640                if len(current_menu.search_text) > 0:
1641                    character_press(event["text"])
1642                    # reset InputHandler so that virtual button
1643                    # is not processed, since we handled this press
1644                    # as a char
1645                    InputHandler.get_button()
1646            else:
1647                character_press(event["text"])
1648        elif event["type"] in ["mouse-motion", "mouse-press", "mouse-release"]:
1649            had_mouse_event = True
1650            handle_mouse_event(event)
1651        else:
1652            print("[WARNING] Unhandled event", event)
1653
1654    AnimationSystem.update()
1655    NotificationRender.update()
1656
1657    # if idle_events_only:
1658    #     # do not update State.get().idle_from
1659    #     pass
1660    # elif len(events) > 0:
1661    #     #print(events)
1662    #     if IDLE:
1663    #         State.get().idle_from = State.get().time + IDLE
1664    #         #print(State.get().idle_from)
1665
1666    if Render.get().dirty or ALWAYS_RENDER:
1667        if Render.get().non_dirty_state:
1668            print("must start rendering again...")
1669        Render.get().non_dirty_state = False
1670        render_func()
1671    else:
1672        # if not Render.get().non_dirty_state:
1673        #     print("pause rendering!")
1674        # Render.get().non_dirty_state = True
1675        Render.get().dirty = True
1676
1677    button = InputHandler.get_button()
1678    if button:
1679        print("InputHandler.get_button", button, InputHandler.last_device)
1680        GameCenter.register_user_activity()
1681        if input_func:
1682            input_func(button)
1683        Render.get().dirty = True
1684    if InputHandler.repeat_info:
1685        Render.get().dirty = True
1686
1687    # if IDLE:
1688    if AnimationSystem.is_active():
1689        # State.get().idle_from = None
1690        Render.get().dirty = True
1691        # elif State.get().idle_from is None:
1692        #     State.get().idle_from = State.get().time + IDLE
1693    if not IDLE:
1694        Render.get().dirty = True
1695    # except KeyboardInterrupt:
1696    #     print "KeyboardInterrupt"
1697    #     return
1698    # return stop_loop
1699    if had_mouse_event:
1700        Render.get().dirty = True
1701
1702    if not Render.get().display_sync:
1703        t = time.time()
1704        diff = t - Render.get().display_last_iteration
1705        # print(diff)
1706        frame_time = 1 / 60.0
1707        if diff < frame_time:
1708            # FIXME: use real refresh rate / frame time
1709            sleep_time = frame_time - diff
1710            time.sleep(sleep_time)
1711        Render.get().display_last_iteration = t
1712
1713    return state.quit
1714