Exemplo n.º 1
0
 def _check_input(self):
     """ Handle keyboard events. """
     while True:
         # s is one unicode char or one scancode
         uc, sc = self.input_handler.get_key()
         if not uc and not sc:
             break
         if uc == eof:
             # ctrl-D (unix) / ctrl-Z (windows)
             backend.input_queue.put(backend.Event(backend.KEYB_QUIT))
         elif uc == u'\x7f':
             # backspace
             backend.input_queue.put(
                 backend.Event(backend.KEYB_DOWN,
                               (eascii.BACKSPACE, scancode.BACKSPACE, [])))
         elif sc or uc:
             # check_full=False to allow pasting chunks of text
             backend.input_queue.put(
                 backend.Event(backend.KEYB_DOWN, (uc, sc, [], False)))
             if sc == scancode.F12:
                 self.f12_active = True
             else:
                 backend.input_queue.put(
                     backend.Event(backend.KEYB_UP, (scancode.F12, )))
                 self.f12_active = False
Exemplo n.º 2
0
 def _handle_text_input(self, event):
     """ Handle text-input event. """
     c = event.text.text.decode('utf-8')
     if self.f11_active:
         # F11+f to toggle fullscreen mode
         if c.upper() == u'F':
             self.fullscreen = not self.fullscreen
             self._do_create_window(*self._find_display_size(
                 self.size[0], self.size[1], self.border_width))
         self.clipboard.handle_key(None, c)
     # the text input event follows the key down event immediately
     elif self.last_down is None:
         # no key down event waiting: other input method
         backend.input_queue.put(backend.Event(backend.KEYB_CHAR, (c, )))
     else:
         eascii, scan, mod, ts = self.last_down
         if eascii:
             c = eascii
         if ts == event.text.timestamp:
             # combine if same time stamp
             backend.input_queue.put(
                 backend.Event(backend.KEYB_DOWN, (c, scan, mod)))
         else:
             # two separate events
             # previous keypress has no corresponding textinput
             self._flush_keypress()
             # current textinput has no corresponding keypress
             backend.input_queue.put(backend.Event(backend.KEYB_CHAR,
                                                   (c, )))
         self.last_down = None
Exemplo n.º 3
0
 def play_noise(self, source, volume, duration, loop=False):
     """ Play a sound on the noise generator. """
     frequency = self.noise_freq[source]
     noise = backend.Event(
         backend.AUDIO_NOISE,
         (source > 3, frequency, duration, 1, loop, volume))
     state.console_state.tone_queue[3].put(noise)
Exemplo n.º 4
0
 def _flush_keypress(self):
     """ Flush last keypress from buffer. """
     if self.last_down is not None:
         # insert into keyboard queue; no text event
         c, scan, mod, _ = self.last_down
         backend.input_queue.put(
             backend.Event(backend.KEYB_DOWN, (c, scan, mod)))
         self.last_down = None
Exemplo n.º 5
0
 def _init_thread(self):
     """ Complete SDL2 interface initialisation. """
     # initialise SDL
     sdl2.SDL_Init(sdl2.SDL_INIT_EVERYTHING)
     # set clipboard handler to SDL2
     backend.clipboard_handler = SDL2Clipboard()
     # display palettes for blink states 0, 1
     self.show_palette = [
         sdl2.SDL_AllocPalette(256),
         sdl2.SDL_AllocPalette(256)
     ]
     # get physical screen dimensions (needs to be called before set_mode)
     display_mode = sdl2.SDL_DisplayMode()
     sdl2.SDL_GetCurrentDisplayMode(0, ctypes.byref(display_mode))
     self.physical_size = display_mode.w, display_mode.h
     # create the window initially, size will be corrected later
     self.display = None
     # create window in same thread that manipulates it
     # "NOTE: You should not expect to be able to create a window, render, or receive events on any thread other than the main one"
     # https://wiki.libsdl.org/CategoryThread
     # http://stackoverflow.com/questions/27751533/sdl2-threading-seg-fault
     self._do_create_window(640, 400)
     # load an all-black 16-colour game palette to get started
     self.set_palette([(0, 0, 0)] * 16, None)
     self.move_cursor(1, 1)
     self.set_page(0, 0)
     self.set_mode(self.kwargs['initial_mode'])
     # support for CGA composite
     self.composite_palette = sdl2.SDL_AllocPalette(256)
     composite_colors = video_graphical.composite_640.get(
         self.composite_card, video_graphical.composite_640['cga'])
     colors = (sdl2.SDL_Color * 256)(
         *[sdl2.SDL_Color(r, g, b, 255) for (r, g, b) in composite_colors])
     sdl2.SDL_SetPaletteColors(self.composite_palette, colors, 0, 256)
     # check if we can honour scaling=smooth
     if self.smooth:
         # pointer to the zoomed surface
         self.zoomed = None
         pixelformat = self.display_surface.contents.format
         if pixelformat.contents.BitsPerPixel != 32:
             logging.warning(
                 'Smooth scaling not available on this display of %d-bit colour depth: needs 32-bit',
                 self.display_surface.format.contents.BitsPerPixel)
             self.smooth = False
         if not hasattr(sdl2, 'sdlgfx'):
             logging.warning(
                 'Smooth scaling not available: SDL_GFX extension not found.'
             )
             self.smooth = False
     # available joysticks
     num_joysticks = sdl2.SDL_NumJoysticks()
     for j in range(num_joysticks):
         sdl2.SDL_JoystickOpen(j)
         # if a joystick is present, its axes report 128 for mid, not 0
         for axis in (0, 1):
             backend.input_queue.put(
                 backend.Event(backend.STICK_MOVED, (j, axis, 128)))
Exemplo n.º 6
0
 def close(self):
     """ Close the audio interface. """
     # drain signal queue (to allow for persistence) and request exit
     if backend.message_queue:
         backend.message_queue.put(backend.Event(backend.AUDIO_QUIT))
         backend.message_queue.join()
     # don't wait for tone que, it will not drain but be pickled later.
     if self.thread and self.thread.is_alive():
         # signal quit and wait for thread to finish
         self.thread.join()
Exemplo n.º 7
0
 def stop_all_sound(self):
     """ Terminate all sounds immediately. """
     for q in state.console_state.tone_queue:
         while not q.empty():
             try:
                 q.get(False)
             except Queue.Empty:
                 continue
             q.task_done()
     backend.message_queue.put(backend.Event(backend.AUDIO_STOP))
Exemplo n.º 8
0
 def _check_input(self):
     """ Handle keyboard events. """
     s = ''
     i = 0
     while True:
         i = self.window.getch()
         if i < 0:
             break
         elif i < 256:
             s += chr(i)
         else:
             if i == curses.KEY_BREAK:
                 # this is fickle, on many terminals doesn't work
                 backend.input_queue.put(
                     backend.Event(backend.KEYB_DOWN,
                                   (u'', scancode.BREAK, [scancode.CTRL])))
             elif i == curses.KEY_RESIZE:
                 self._resize(self.height, self.width)
             # scancode, insert here and now
             # there shouldn't be a mix of special keys and utf8 in one
             # uninterrupted string, since the only reason an uninterrupted
             # string would be longer than 1 char is because it's a single
             # utf-8 sequence or a pasted utf-8 string, neither of which
             # can contain special characters.
             # however, if that does occur, this won't work correctly.
             scan = curses_to_scan.get(i, None)
             c = curses_to_eascii.get(i, '')
             if scan or c:
                 backend.input_queue.put(
                     backend.Event(backend.KEYB_DOWN, (c, scan, [])))
                 if i == curses.KEY_F12:
                     self.f12_active = True
                 else:
                     self._unset_f12()
     # convert into unicode chars
     u = s.decode(encoding, 'replace')
     # then handle these one by one
     for c in u:
         #check_full=False to allow pasting chunks of text
         backend.input_queue.put(
             backend.Event(backend.KEYB_DOWN, (c, None, [], False)))
         self._unset_f12()
Exemplo n.º 9
0
 def _check_input(self):
     """ Handle screen and interface events. """
     # check and handle pygame events
     for event in pygame.event.get():
         if event.type == pygame.KEYDOWN:
             self._handle_key_down(event)
         if event.type == pygame.KEYUP:
             self._handle_key_up(event)
         elif event.type == pygame.MOUSEBUTTONDOWN:
             # copy, paste and pen may be on the same button, so no elifs
             if event.button == self.mousebutton_copy:
                 # LEFT button: copy
                 pos = self._normalise_pos(*event.pos)
                 self.clipboard.start(
                     1 + pos[1] // self.font_height,
                     1 + (pos[0] + self.font_width // 2) // self.font_width)
             if event.button == self.mousebutton_paste:
                 # MIDDLE button: paste
                 self.clipboard.paste(mouse=True)
             if event.button == self.mousebutton_pen:
                 # right mouse button is a pen press
                 backend.input_queue.put(
                     backend.Event(backend.PEN_DOWN,
                                   self._normalise_pos(*event.pos)))
         elif event.type == pygame.MOUSEBUTTONUP:
             backend.input_queue.put(backend.Event(backend.PEN_UP))
             if event.button == self.mousebutton_copy:
                 self.clipboard.copy(mouse=True)
                 self.clipboard.stop()
         elif event.type == pygame.MOUSEMOTION:
             pos = self._normalise_pos(*event.pos)
             backend.input_queue.put(backend.Event(backend.PEN_MOVED, pos))
             if self.clipboard.active():
                 self.clipboard.move(
                     1 + pos[1] // self.font_height,
                     1 + (pos[0] + self.font_width // 2) // self.font_width)
         elif event.type == pygame.JOYBUTTONDOWN:
             backend.input_queue.put(
                 backend.Event(backend.STICK_DOWN,
                               (event.joy, event.button)))
         elif event.type == pygame.JOYBUTTONUP:
             backend.input_queue.put(
                 backend.Event(backend.STICK_UP, (event.joy, event.button)))
         elif event.type == pygame.JOYAXISMOTION:
             backend.input_queue.put(
                 backend.Event(
                     backend.STICK_MOVED,
                     (event.joy, event.axis, int(event.value * 127 + 128))))
         elif event.type == pygame.VIDEORESIZE:
             self.fullscreen = False
             self._resize_display(event.w, event.h)
         elif event.type == pygame.QUIT:
             if self.nokill:
                 self.set_caption_message(
                     'to exit type <CTRL+BREAK> <ESC> SYSTEM')
             else:
                 backend.input_queue.put(backend.Event(backend.KEYB_QUIT))
Exemplo n.º 10
0
 def _handle_key_up(self, e):
     """ Handle key-up event. """
     if e.key == pygame.K_F11:
         self.clipboard.stop()
         self.f11_active = False
     # last key released gets remembered
     try:
         backend.input_queue.put(
             backend.Event(backend.KEYB_UP, (key_to_scan[e.key], )))
     except KeyError:
         pass
Exemplo n.º 11
0
 def close(self):
     """ Close the interface. """
     # drain signal queue (to allow for persistence) and request exit
     # signal quit and wait for thread to finish
     if backend.video_queue:
         backend.video_queue.put(backend.Event(backend.VIDEO_QUIT))
         backend.video_queue.join()
     if self.thread and self.thread.is_alive():
         # signal quit and wait for thread to finish
         self.thread.join()
         self.thread = None
Exemplo n.º 12
0
 def copy(self, mouse=False):
     """ Copy screen characters from selection into clipboard. """
     start, stop = self.select_start, self.select_stop
     if not start or not stop:
         return
     if start[0] == stop[0] and start[1] == stop[1]:
         return
     if start[0] > stop[0] or (start[0] == stop[0] and start[1] > stop[1]):
         start, stop = stop, start
     backend.input_queue.put(
         backend.Event(backend.CLIP_COPY,
                       (start[0], start[1], stop[0], stop[1], mouse)))
Exemplo n.º 13
0
 def _handle_key_down(self, e):
     """ Handle key-down event. """
     # get scancode
     scan = key_to_scan.get(e.key, None)
     # get modifiers
     mod = [s for m, s in mod_to_scan.iteritems() if e.mod & m]
     # get eascii
     try:
         if e.mod & pygame.KMOD_LALT or (not self.altgr
                                         and e.mod & pygame.KMOD_RALT):
             c = alt_key_to_eascii[e.key]
         elif e.mod & pygame.KMOD_CTRL:
             c = ctrl_key_to_eascii[e.key]
         elif e.mod & pygame.KMOD_SHIFT:
             c = shift_key_to_eascii[e.key]
         else:
             c = key_to_eascii[e.key]
     except KeyError:
         key = e.key
         if e.mod & pygame.KMOD_CTRL and key >= ord('a') and key <= ord(
                 'z'):
             c = unichr(key - ord('a') + 1)
         elif e.mod & pygame.KMOD_CTRL and key >= ord('[') and key <= ord(
                 '_'):
             c = unichr(key - ord('A') + 1)
         else:
             c = e.unicode
     # handle F11 home-key
     if e.key == pygame.K_F11:
         self.f11_active = True
         self.clipboard.start(self.cursor_row, self.cursor_col)
     elif self.f11_active:
         # F11+f to toggle fullscreen mode
         if e.key == pygame.K_f:
             self.fullscreen = not self.fullscreen
             self._resize_display(*self._find_display_size(
                 self.size[0], self.size[1], self.border_width))
         self.clipboard.handle_key(scan, c)
     else:
         # double NUL characters, as single NUL signals e-ASCII
         if c == u'\0':
             c = eascii.NUL
         # fix missing ascii for numeric keypad on Windows
         if e.mod & pygame.KMOD_NUM:
             try:
                 c = key_to_eascii_num[e.key]
             except KeyError:
                 pass
         # insert into keyboard queue
         backend.input_queue.put(
             backend.Event(backend.KEYB_DOWN, (c, scan, mod)))
Exemplo n.º 14
0
 def _check_input(self):
     """ Handle keyboard events. """
     # avoid blocking on ttys if there's no input
     if plat.stdin_is_tty and not kbhit():
         return
     # NOTE: errors occur when backspace is used with text input
     # only the last byte is erased, not the whole utf-8 sequence
     s = sys.stdin.readline().decode(encoding, errors='ignore')
     if s == '':
         redirect.input_closed = True
     for c in s:
         # replace LF -> CR if needed
         if c == u'\n' and lf_to_cr:
             c = u'\r'
         # check_full=False as all input may come at once
         backend.input_queue.put(
             backend.Event(backend.KEYB_CHAR, (c, False)))
Exemplo n.º 15
0
 def play_sound_no_wait(self,
                        frequency,
                        duration,
                        fill=1,
                        loop=False,
                        voice=0,
                        volume=15):
     """ Play a sound on the tone generator. """
     if frequency < 0:
         frequency = 0
     if ((pcjr_sound == 'tandy' or (pcjr_sound == 'pcjr' and self.sound_on))
             and frequency < 110. and frequency != 0):
         # pcjr, tandy play low frequencies as 110Hz
         frequency = 110.
     tone = backend.Event(backend.AUDIO_TONE,
                          (frequency, duration, fill, loop, volume))
     state.console_state.tone_queue[voice].put(tone)
     if voice == 2 and frequency != 0:
         # reset linked noise frequencies
         # /2 because we're using a 0x4000 rotation rather than 0x8000
         self.noise_freq[3] = frequency / 2.
         self.noise_freq[7] = frequency / 2.
Exemplo n.º 16
0
 def paste(self, mouse=False):
     """ Paste from clipboard into keyboard buffer. """
     backend.input_queue.put(backend.Event(backend.CLIP_PASTE, (mouse, )))
Exemplo n.º 17
0
 def __init__(self, **kwargs):
     """ Initialise pygame interface. """
     video_graphical.VideoGraphical.__init__(self, **kwargs)
     # set state objects to whatever is now in state (may have been unpickled)
     if not pygame:
         logging.warning('PyGame module not found.')
         raise video.InitFailed()
     if not numpy:
         logging.debug('NumPy module not found.')
         raise video.InitFailed()
     pygame.init()
     try:
         # poll the driver to force an exception if not initialised
         pygame.display.get_driver()
     except pygame.error:
         self._close_pygame()
         logging.warning('No suitable display driver for PyGame.')
         raise video.InitFailed()
     # display & border
     # display buffer
     self.canvas = []
     # border attribute
     self.border_attr = 0
     # palette and colours
     # composite colour artifacts
     self.composite_artifacts = False
     # working palette - attribute index in blue channel
     self.work_palette = [(0, 0, index) for index in range(256)]
     # display palettes for blink states 0, 1
     self.show_palette = [None, None]
     # composite palette
     try:
         self.composite_640_palette = video_graphical.composite_640[
             self.composite_card]
     except KeyError:
         self.composite_640_palette = video_graphical.composite_640['cga']
     # text attributes supported
     self.mode_has_blink = True
     # update cycle
     # update flag
     self.screen_changed = True
     # refresh cycle parameters
     self.cycle = 0
     self.last_cycle = 0
     self.cycle_time = 120
     self.blink_cycles = 5
     # cursor
     # cursor shape
     self.cursor = None
     # current cursor location
     self.last_row = 1
     self.last_col = 1
     # cursor is visible
     self.cursor_visible = True
     # buffer for text under cursor
     self.under_top_left = None
     # fonts
     # prebuilt glyphs
     self.glyph_dict = {}
     # joystick and mouse
     # available joysticks
     self.joysticks = []
     #
     # get physical screen dimensions (needs to be called before set_mode)
     display_info = pygame.display.Info()
     self.physical_size = display_info.current_w, display_info.current_h
     # determine initial display size
     self.display_size = self._find_display_size(640, 480,
                                                 self.border_width)
     self._set_icon(kwargs['icon'])
     # first set the screen non-resizeable, to trick things like maximus into not full-screening
     # I hate it when applications do this ;)
     try:
         if not self.fullscreen:
             pygame.display.set_mode(self.display_size, 0)
         self._resize_display(*self.display_size)
     except pygame.error:
         self._close_pygame()
         logging.warning('Could not initialise PyGame display')
         raise video.InitFailed()
     if self.smooth and self.display.get_bitsize() < 24:
         logging.warning(
             "Smooth scaling not available on this display (depth %d < 24)",
             self.display.get_bitsize())
         self.smooth = False
     pygame.display.set_caption(self.caption)
     pygame.key.set_repeat(500, 24)
     # load an all-black 16-colour game palette to get started
     self.set_palette([(0, 0, 0)] * 16, None)
     pygame.joystick.init()
     self.joysticks = [
         pygame.joystick.Joystick(x)
         for x in range(pygame.joystick.get_count())
     ]
     for j in self.joysticks:
         j.init()
     # if a joystick is present, its axes report 128 for mid, not 0
     for joy in range(len(self.joysticks)):
         for axis in (0, 1):
             backend.input_queue.put(
                 backend.Event(backend.STICK_MOVED, (joy, axis, 128)))
     # mouse setups
     buttons = {'left': 1, 'middle': 2, 'right': 3, 'none': -1}
     copy_paste = kwargs.get('copy-paste', ('left', 'middle'))
     self.mousebutton_copy = buttons[copy_paste[0]]
     self.mousebutton_paste = buttons[copy_paste[1]]
     self.mousebutton_pen = buttons[kwargs.get('pen', 'right')]
     self.move_cursor(0, 0)
     self.set_page(0, 0)
     self.set_mode(kwargs['initial_mode'])
     self.f11_active = False
     self.altgr = kwargs['altgr']
     if not self.altgr:
         key_to_scan[pygame.K_RALT] = scancode.ALT
         mod_to_scan[pygame.KMOD_RALT] = scancode.ALT
     backend.clipboard_handler = get_clipboard_handler()
Exemplo n.º 18
0
 def _unset_f12(self):
     """ Deactivate F12 """
     if self.f12_active:
         backend.input_queue.put(
             backend.Event(backend.KEYB_UP, (scancode.F12, )))
         self.f12_active = False
Exemplo n.º 19
0
 def _check_input(self):
     """ Handle screen and interface events. """
     # check and handle input events
     self.last_down = None
     event = sdl2.SDL_Event()
     while sdl2.SDL_PollEvent(ctypes.byref(event)) != 0:
         if event.type == sdl2.SDL_KEYDOWN:
             self._handle_key_down(event)
         elif event.type == sdl2.SDL_KEYUP:
             self._handle_key_up(event)
         elif event.type == sdl2.SDL_TEXTINPUT:
             self._handle_text_input(event)
         elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
             pos = self._normalise_pos(event.button.x, event.button.y)
             # copy, paste and pen may be on the same button, so no elifs
             if event.button.button == self.mousebutton_copy:
                 # LEFT button: copy
                 self.clipboard.start(
                     1 + pos[1] // self.font_height,
                     1 + (pos[0] + self.font_width // 2) // self.font_width)
             if event.button.button == self.mousebutton_paste:
                 # MIDDLE button: paste
                 self.clipboard.paste(mouse=True)
             if event.button.button == self.mousebutton_pen:
                 # right mouse button is a pen press
                 backend.input_queue.put(
                     backend.Event(backend.PEN_DOWN, pos))
         elif event.type == sdl2.SDL_MOUSEBUTTONUP:
             backend.input_queue.put(backend.Event(backend.PEN_UP))
             if event.button.button == self.mousebutton_copy:
                 self.clipboard.copy(mouse=True)
                 self.clipboard.stop()
         elif event.type == sdl2.SDL_MOUSEMOTION:
             pos = self._normalise_pos(event.motion.x, event.motion.y)
             backend.input_queue.put(backend.Event(backend.PEN_MOVED, pos))
             if self.clipboard.active():
                 self.clipboard.move(
                     1 + pos[1] // self.font_height,
                     1 + (pos[0] + self.font_width // 2) // self.font_width)
         elif event.type == sdl2.SDL_JOYBUTTONDOWN:
             backend.input_queue.put(
                 backend.Event(backend.STICK_DOWN,
                               (event.jbutton.which, event.jbutton.button)))
         elif event.type == sdl2.SDL_JOYBUTTONUP:
             backend.input_queue.put(
                 backend.Event(backend.STICK_UP,
                               (event.jbutton.which, event.jbutton.button)))
         elif event.type == sdl2.SDL_JOYAXISMOTION:
             backend.input_queue.put(
                 backend.Event(
                     backend.STICK_MOVED,
                     (event.jaxis.which, event.jaxis.axis,
                      int((event.jaxis.value / 32768.) * 127 + 128))))
         elif event.type == sdl2.SDL_WINDOWEVENT:
             if event.window.event == sdl2.SDL_WINDOWEVENT_RESIZED:
                 self._resize_display(event.window.data1,
                                      event.window.data2)
             # unset Alt modifiers on entering/leaving the window
             # workaround for what seems to be an SDL2 bug
             # where the ALT modifier sticks on the first Alt-Tab out
             # of the window
             elif event.window.event in (sdl2.SDL_WINDOWEVENT_LEAVE,
                                         sdl2.SDL_WINDOWEVENT_ENTER,
                                         sdl2.SDL_WINDOWEVENT_FOCUS_LOST,
                                         sdl2.SDL_WINDOWEVENT_FOCUS_GAINED):
                 sdl2.SDL_SetModState(sdl2.SDL_GetModState()
                                      & ~sdl2.KMOD_ALT)
         elif event.type == sdl2.SDL_QUIT:
             if self.nokill:
                 self.set_caption_message(
                     'to exit type <CTRL+BREAK> <ESC> SYSTEM')
             else:
                 backend.input_queue.put(backend.Event(backend.KEYB_QUIT))
     self._flush_keypress()
Exemplo n.º 20
0
 def persist(self, flag):
     """ Set mixer persistence flag (runmode). """
     backend.message_queue.put(backend.Event(backend.AUDIO_PERSIST, flag))