view pytouhou/menu.py @ 612:73f134f84c7f

Request a RGB888 context, since SDL2’s default of RGB332 sucks. On X11/GLX, it will select the first config available, that is the best one, while on EGL it will iterate over them to select the one closest to what the application requested. Of course, anything lower than RGB888 looks bad and we really don’t want that.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 26 Mar 2015 20:20:37 +0100
parents 7f113f15300b
children d1f0bb0b7a17
line wrap: on
line source

# -*- encoding: utf-8 -*-
##
## Copyright (C) 2014 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##

from pytouhou.utils.helpers import get_logger
logger = get_logger(__name__)

from gi.repository import Gtk, Gdk, GLib

import sys
import os
import re

GL_VERSION_REGEX = re.compile(r'^\d\.\d$')


class Handler(object):
    def __init__(self, config, args):
        self.config = config
        self.args = args

    def init_gtk(self, builder):
        self.start_window = builder.get_object('start_window')
        self.game_window = builder.get_object('game_window')
        self.options_window = builder.get_object('options_window')

        self.replay_filechooserdialog = builder.get_object('replay_filechooserdialog')

        # Game widgets
        self.difficulty_box = builder.get_object('difficulty_box')
        self.character_box = builder.get_object('character_box')
        self.stage_box = builder.get_object('stage_box')
        self.boss_rush_checkbutton = builder.get_object('boss_rush_checkbutton')

        self.difficulty_comboboxtext = builder.get_object('difficulty_comboboxtext')
        self.character_comboboxtext = builder.get_object('character_comboboxtext')
        self.stage_comboboxtext = builder.get_object('stage_comboboxtext')

        # Options widgets
        self.path_filechooserbutton = builder.get_object('path_filechooserbutton')
        self.backend_comboboxtext = builder.get_object('backend_comboboxtext')

        # OpenGL backend
        self.opengl_grid = builder.get_object('opengl_grid')
        self.flavor_comboboxtext = builder.get_object('flavor_comboboxtext')
        self.version_entry = builder.get_object('version_entry')
        self.double_buffer_checkbutton = builder.get_object('double_buffer_checkbutton')

        # SDL backend
        self.sdl_grid = builder.get_object('sdl_grid')

        self.fps_entry = builder.get_object('fps_entry')
        self.no_background_checkbutton = builder.get_object('no_background_checkbutton')
        self.no_particles_checkbutton = builder.get_object('no_particles_checkbutton')
        self.no_sound_checkbutton = builder.get_object('no_sound_checkbutton')

        self.difficulty_comboboxtext.set_active_id(str(self.args.rank))
        self.character_comboboxtext.set_active_id(str(self.args.character))
        self.stage_comboboxtext.set_active_id(str(self.args.stage))
        self.boss_rush_checkbutton.set_active(self.args.boss_rush)

        self.path_filechooserbutton.set_filename(self.args.path)
        self.backend_comboboxtext.set_active_id(' '.join(self.args.backend))

        self.flavor_comboboxtext.set_active_id(self.args.gl_flavor)
        self.version_entry.set_text(str(self.args.gl_version))
        if self.args.double_buffer is None:
            self.double_buffer_checkbutton.set_inconsistent(True)
        else:
            self.double_buffer_checkbutton.set_inconsistent(False)
            self.double_buffer_checkbutton.set_active(self.args.double_buffer)

        self.fps_entry.set_text(str(self.args.fps_limit))
        self.no_background_checkbutton.set_active(self.args.no_background)
        self.no_particles_checkbutton.set_active(self.args.no_particles)
        self.no_sound_checkbutton.set_active(self.args.no_sound)

    def hide_and_play(self, window):
        window.hide()
        Gtk.main_quit()
        print('Play!')

    def on_quit(self, *args):
        Gtk.main_quit(*args)
        sys.exit(0)


    # Main menu

    def on_start_window_key_press_event(self, window, event_key):
        if (event_key.keyval == Gdk.KEY_Escape or
                event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q):
            self.on_quit()

    def on_start_button_clicked(self, _):
        self.difficulty_box.show()
        self.character_box.show()
        self.stage_box.hide()

        self.stage_comboboxtext.set_active_id(None)
        self.args.stage = None

        self.start_window.hide()
        self.game_window.show()

    def on_extra_start_button_clicked(self, _):
        self.difficulty_box.hide()
        self.character_box.show()
        self.stage_box.hide()

        self.difficulty_comboboxtext.set_active_id('4')
        self.stage_comboboxtext.set_active_id('7')

        self.start_window.hide()
        self.game_window.show()

    def on_practice_start_button_clicked(self, _):
        self.difficulty_box.show()
        self.character_box.show()
        self.stage_box.show()

        self.start_window.hide()
        self.game_window.show()

    def on_options_button_clicked(self, _):
        self.start_window.hide()
        self.options_window.show()

    def on_replay_button_clicked(self, _):
        self.start_window.hide()
        self.replay_filechooserdialog.show()

    def on_inactive_button_clicked(self, _):
        raise NotImplementedError('Menu not implemented')

    on_netplay_start_button_clicked = on_inactive_button_clicked
    on_score_button_clicked = on_inactive_button_clicked
    on_music_room_button_clicked = on_inactive_button_clicked


    # Game menu

    def on_game_back_button_clicked(self, _):
        self.game_window.hide()
        self.start_window.show()

    def on_play_button_clicked(self, _):
        self.hide_and_play(self.game_window)

    def on_game_window_key_press_event(self, window, event_key):
        if event_key.keyval == Gdk.KEY_Escape:
            self.game_window.hide()
            self.start_window.show()
        elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q:
            self.on_quit()

    def on_difficulty_comboboxtext_changed(self, item):
        active = item.get_active_id()
        difficulty = int(active)
        self.config.set('rank', active if difficulty < 4 else None)
        self.args.rank = difficulty

    def on_character_comboboxtext_changed(self, item):
        character = int(item.get_active_id())
        self.config.set('character', character)
        self.args.character = character

    def on_stage_comboboxtext_changed(self, item):
        stage = item.get_active_id()
        self.args.stage = int(stage) if stage is not None else None

    def on_boss_rush_checkbutton_toggled(self, boss_rush_checkbutton):
        active = boss_rush_checkbutton.get_active()
        self.config.set('boss-rush', active)
        self.args.boss_rush = active


    # Replay dialog

    def on_replay_filechooserdialog_close(self, _):
        self.replay_filechooserdialog.hide()
        self.start_window.show()

    on_replay_cancel_button_clicked = on_replay_filechooserdialog_close

    def on_replay_open_button_clicked(self, button):
        try:
            open(self.args.replay, 'rb').close()
        except (IOError, TypeError):
            return
        self.hide_and_play(self.replay_filechooserdialog)

    def on_replay_filechooserdialog_selection_changed(self, dialog):
        self.args.replay = dialog.get_filename()

    def on_replay_filechooserdialog_file_activated(self, window):
        self.args.replay = window.get_filename()
        self.hide_and_play(self.replay_filechooserdialog)

    def on_replay_filechooserdialog_key_press_event(self, window, event_key):
        if event_key.keyval == Gdk.KEY_Escape:
            self.replay_filechooserdialog.hide()
            self.start_window.show()
        elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q:
            self.on_quit()


    # Options menu

    def on_options_back_button_clicked(self, _):
        self.options_window.hide()
        self.start_window.show()

    def on_options_window_key_press_event(self, window, event_key):
        if event_key.keyval == Gdk.KEY_Escape:
            self.options_window.hide()
            self.start_window.show()
        elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q:
            self.on_quit()

    def on_path_filechooserbutton_file_set(self, path_filechooserbutton):
        path = path_filechooserbutton.get_filename()
        self.config.set('path', path)

    def on_backend_comboboxtext_changed(self, backend_comboboxtext):
        active = backend_comboboxtext.get_active_id()
        self.config.set('backend', active)
        backends = active.split()
        new_grids = [getattr(self, backend + '_grid') for backend in backends]
        for grid in [self.opengl_grid, self.sdl_grid]:
            if grid not in new_grids:
                grid.hide()
            else:
                grid.show()

    def on_flavor_comboboxtext_changed(self, flavor_comboboxtext):
        active = flavor_comboboxtext.get_active_id()
        self.config.set('gl-flavor', active)

    def on_version_entry_changed(self, version_entry):
        text = version_entry.get_text()
        if not GL_VERSION_REGEX.match(text):
            raise ValueError('“%s” is not <digit>.<digit>' % text)
        self.config.set('gl-version', text)

    def on_double_buffer_checkbutton_clicked(self, double_buffer_checkbutton):
        inconsistent = double_buffer_checkbutton.get_inconsistent()
        active = double_buffer_checkbutton.get_active()
        if inconsistent:
            active = False
            inconsistent = False
        elif active:
            active = True
        else:
            inconsistent = True
        double_buffer_checkbutton.set_active(active)
        double_buffer_checkbutton.set_inconsistent(inconsistent)
        self.config.set('double-buffer', None if inconsistent else active)

    def on_fps_entry_changed(self, fps_entry):
        text = fps_entry.get_text()
        try:
            int(text)
        except ValueError:
            raise ValueError('“%s” is not integer' % text)
        else:
            self.config.set('fps-limit', text)

    def on_no_background_checkbutton_toggled(self, checkbutton):
        active = checkbutton.get_active()
        self.config.set('no-background', not active)

    def on_no_particles_checkbutton_toggled(self, checkbutton):
        active = checkbutton.get_active()
        self.config.set('no-particles', active)

    def on_no_sound_checkbutton_toggled(self, checkbutton):
        active = checkbutton.get_active()
        self.config.set('no-sound', active)


def menu(config, args):
    assert Gtk
    handler = Handler(config, args)

    builder = Gtk.Builder()
    try:
        builder.add_from_file(os.path.join(os.path.dirname(__file__), 'data', 'menu.glade'))
    except GLib.GError:
        builder.add_from_file(os.path.join('data', 'menu.glade'))
    builder.connect_signals(handler)

    handler.init_gtk(builder)

    Gtk.main()