booglybob / pyglet

Automatically exported from code.google.com/p/pyglet
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Pressing Alt key hangs any pyglet application #462

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Pressing the Alt key hangs any Pyglet application until Alt is pressed again.

When I say 'hang', I mean all clock scheduled function calls and screen
refreshes seem to stop happening. The application starts updating normally
again if Alt is pressed again, or if the mouse cursor is clicked on the
window. Pressing any other key also unhangs it, with an MSWindows error
beep. (the same beep that happens if you press Alt in any other
application, such as Firefox, and then press a letter that does NOT
correspond to any of the pull-down menu keyboard shortcuts)

This doesn't happen if my application is fullscreen, it only happens in
windowed mode.

The hang seems to occur right after my handlers of the
window.on_key_release event.

I'm on Windows XP, Python 2.6.4, Pyglet 1.1.4, 1.1.3 or 1.1.2.

In case it is a useful clue, under 1.1.1 and 1.1, the same behaviour can be
provoked, but it doesn't happen at all until after you have moved the
window by dragging the window titlebar in the usual fashion. Moving the
window is the only action I can find that will provoke this behaviour. Alt
key hanging is not induced in v1.1 or v1.1.1 by clicking the titlebar, nor
clicking elseshere, nor by losing and regaining focus. 

I've tried a couple of wild guesses at workarounds, such as setting
exclusive keyboard for the application's window, or else returning
EVENT_HANDLED from the on_key_press or on_key_release handlers, but they
don't seem to help.

pyglet-user Hello3171 is also using WindowsXP, and confirmed he also sees
this behaviour:
http://groups.google.com/group/pyglet-users/browse_frm/thread/5608bad6a6c543e5#

Python
------------------------------------------------------------------------------
sys.version: 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit
(Intel)]
sys.platform: win32
os.getcwd(): f:\Documents and Settings\jhartley\Desktop\pyglet-1.1.4

pyglet
------------------------------------------------------------------------------
pyglet.version: 1.1.4
pyglet.__file__: pyglet\__init__.pyc
pyglet.options['debug_trace_depth'] = 1
pyglet.options['font'] = ('gdiplus', 'win32')
pyglet.options['xsync'] = True
pyglet.options['debug_trace_flush'] = True
pyglet.options['debug_win32'] = False
pyglet.options['debug_gl_trace'] = False
pyglet.options['debug_x11'] = False
pyglet.options['shadow_window'] = True
pyglet.options['debug_font'] = False
pyglet.options['debug_media'] = False
pyglet.options['debug_trace'] = False
pyglet.options['debug_lib'] = False
pyglet.options['graphics_vbo'] = True
pyglet.options['vsync'] = None
pyglet.options['debug_trace_args'] = False
pyglet.options['debug_gl'] = True
pyglet.options['debug_graphics_batch'] = False
pyglet.options['audio'] = ('directsound', 'openal', 'alsa', 'silent')
pyglet.options['debug_texture'] = False
pyglet.options['debug_gl_trace_args'] = False

pyglet.window
------------------------------------------------------------------------------
platform: <pyglet.window.win32.Win32Platform object at 0x00DE29B0>
display: <pyglet.window.win32.Win32Display object at 0x00DE2930>
screens[0]: Win32Screen(x=0, y=0, width=1680, height=1050)
config['double_buffer'] = True
config['stereo'] = False
config['buffer_size'] = 32
config['aux_buffers'] = 0
config['sample_buffers'] = 0
config['samples'] = 0
config['red_size'] = 8
config['green_size'] = 8
config['blue_size'] = 8
config['alpha_size'] = 8
config['depth_size'] = 24
config['stencil_size'] = 8
config['accum_red_size'] = 0
config['accum_green_size'] = 0
config['accum_blue_size'] = 0
config['accum_alpha_size'] = 0
context: Win32Context()

pyglet.gl.gl_info
------------------------------------------------------------------------------
gl_info.get_version(): 2.1.7980 Release
gl_info.get_vendor(): ATI Technologies Inc.
gl_info.get_renderer(): ATI Mobility Radeon X1400
gl_info.get_extensions():
   GL_AMD_performance_monitor
   GL_ARB_depth_texture
   GL_ARB_draw_buffers
   GL_ARB_fragment_program
   GL_ARB_fragment_shader
   GL_ARB_multisample
   GL_ARB_multitexture
   GL_ARB_occlusion_query
   GL_ARB_pixel_buffer_object
   GL_ARB_point_parameters
   GL_ARB_point_sprite
   GL_ARB_shader_objects
   GL_ARB_shading_language_100
   GL_ARB_shadow
   GL_ARB_shadow_ambient
   GL_ARB_texture_border_clamp
   GL_ARB_texture_compression
   GL_ARB_texture_cube_map
   GL_ARB_texture_env_add
   GL_ARB_texture_env_combine
   GL_ARB_texture_env_crossbar
   GL_ARB_texture_env_dot3
   GL_ARB_texture_float
   GL_ARB_texture_mirrored_repeat
   GL_ARB_texture_non_power_of_two
   GL_ARB_texture_rectangle
   GL_ARB_transpose_matrix
   GL_ARB_vertex_buffer_object
   GL_ARB_vertex_program
   GL_ARB_vertex_shader
   GL_ARB_window_pos
   GL_ATI_draw_buffers
   GL_ATI_envmap_bumpmap
   GL_ATI_fragment_shader
   GL_ATI_meminfo
   GL_ATI_separate_stencil
   GL_ATI_shader_texture_lod
   GL_ATI_texture_compression_3dc
   GL_ATI_texture_env_combine3
   GL_ATI_texture_float
   GL_EXT_abgr
   GL_EXT_bgra
   GL_EXT_blend_color
   GL_EXT_blend_equation_separate
   GL_EXT_blend_func_separate
   GL_EXT_blend_minmax
   GL_EXT_blend_subtract
   GL_EXT_compiled_vertex_array
   GL_EXT_copy_texture
   GL_EXT_draw_range_elements
   GL_EXT_fog_coord
   GL_EXT_framebuffer_blit
   GL_EXT_framebuffer_multisample
   GL_EXT_framebuffer_object
   GL_EXT_gpu_program_parameters
   GL_EXT_multi_draw_arrays
   GL_EXT_packed_depth_stencil
   GL_EXT_packed_pixels
   GL_EXT_point_parameters
   GL_EXT_rescale_normal
   GL_EXT_secondary_color
   GL_EXT_separate_specular_color
   GL_EXT_shadow_funcs
   GL_EXT_stencil_wrap
   GL_EXT_subtexture
   GL_EXT_texgen_reflection
   GL_EXT_texture3D
   GL_EXT_texture_compression_s3tc
   GL_EXT_texture_cube_map
   GL_EXT_texture_edge_clamp
   GL_EXT_texture_env_add
   GL_EXT_texture_env_combine
   GL_EXT_texture_env_dot3
   GL_EXT_texture_filter_anisotropic
   GL_EXT_texture_lod_bias
   GL_EXT_texture_mirror_clamp
   GL_EXT_texture_object
   GL_EXT_texture_rectangle
   GL_EXT_texture_sRGB
   GL_EXT_vertex_array
   GL_KTX_buffer_region
   GL_NV_blend_square
   GL_NV_texgen_reflection
   GL_SGIS_generate_mipmap
   GL_SGIS_texture_edge_clamp
   GL_SGIS_texture_lod
   GL_WIN_swap_hint
   WGL_EXT_swap_control

pyglet.gl.glu_info
------------------------------------------------------------------------------
glu_info.get_version(): 1.2.2.0 Microsoft Corporation
glu_info.get_extensions():
   GL_EXT_bgra

pyglet.gl.glx_info
------------------------------------------------------------------------------
GLX not available.

pyglet.media
------------------------------------------------------------------------------
driver: pyglet.media.drivers.directsound

pyglet.media.avbin
------------------------------------------------------------------------------
AVbin not available.

pyglet.media.drivers.openal
------------------------------------------------------------------------------
OpenAL not available.

Original issue reported on code.google.com by tart...@gmail.com on 12 Jan 2010 at 1:10

GoogleCodeExporter commented 8 years ago
I say 'any pyglet application', but just in case, here's a minimal repro script 
I was
using to investigate it. The key handlers are not required to reproduce, I was 
just
adding them to investigate precisely when the hang seems to occur.

Best regards,
  Jonathan

import random
from pyglet import app, clock
from pyglet.event import EVENT_HANDLED
from pyglet.window import Window

win = None

def on_draw():
    print 'draw', random.randint(0, 10)
    win.clear()

def update(_):
    print 'update', random.randint(0, 10)

def on_key_press(symbol, modifiers):
    print 'key press', symbol, modifiers

def on_key_release(symbol, modifiers):
    print 'key release', symbol, modifiers
    return EVENT_HANDLED

win = Window(fullscreen=False)
win.on_draw = on_draw
win.on_key_press = on_key_press
win.on_key_release = on_key_release
clock.schedule(update)
app.run()

Original comment by tart...@gmail.com on 12 Jan 2010 at 1:15

GoogleCodeExporter commented 8 years ago
I can't reproduce it on Linux using your script.

Original comment by useboxnet on 17 Jul 2013 at 5:05

GoogleCodeExporter commented 8 years ago
This is definitely an issue only on Windows. Sorry if this wasn't clear.

The beep you get is the same one that Windows produces when it intercepts a 
keypress without handing it to the application (e,g, for a window layout 
operation like maximise or using pull-down menus) but then realises that the 
keypress is invalid for some reason. I imagine Windows has suspended the 
application while it thinks it's handing some pull-down menu operation, even 
though the app has no pull-downs defined.

Original comment by tart...@gmail.com on 18 Jul 2013 at 7:15

GoogleCodeExporter commented 8 years ago
Also: At the time I reported this, I was running windows XP. I'll try it again 
on Windows 7 and report back...

Original comment by tart...@gmail.com on 18 Jul 2013 at 7:16

GoogleCodeExporter commented 8 years ago

Original comment by Adam.JT...@gmail.com on 19 Jul 2013 at 11:31

GoogleCodeExporter commented 8 years ago

Original comment by useboxnet on 21 Sep 2013 at 10:37

GoogleCodeExporter commented 8 years ago
I have this issue as well on my Windows 8.1 system.  I am sure this will affect 
all windows users.  As predicted by "tart", this seems to be related to the 
lack of a window menu.  I have made a small patch and was able to fix this 
problem.  This patch removes the window menu for the the default window style.

Original comment by Leif.The...@gmail.com on 20 Dec 2014 at 10:07

Attachments:

GoogleCodeExporter commented 8 years ago
Turns out earlier my patch removes the close button in the window decoration.  
I didn't catch that right away since my window manager removes window 
decoration.  I've revised my patch now.  On closer examination, this issue was 
related to an unreported bug where events would be handled incorrectly.  It has 
been resolved by implementing a check for the ALT key, fixing the bug, along 
with verifying that all event handlers conform.  I've outlined the changes and 
rationale below.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633572%28v=vs.85%29.as
px
According to windows api, the application should return 0 if the app handles 
the event, and should call user32.DefWindowProcW(hwnd, msg, wParam, lParam) to 
provide default processing for any window messages that an application does not 
process.  In order to prevent the pyglet app from hanging when the ALT key is 
pressed, we must provide a handler for WM_SYSCOMMAND and to check for the key 
then return 0 so that windows does not attempt to open the window menu bar and 
hang.

https://code.google.com/p/pyglet/source/browse/pyglet/window/win32/__init__.py#6
09
Event handlers should be able to return a value, and if that value is 0, pass 
it on to the Window Procedure to halt processing of the event.  Non-zero values 
are also valid, but have a different meaning depending on the context of the 
message.  For the purposes of this issue, that is not important.  It seems that 
the intended behaviour of the event handlers is to allow the individual 
functions to return 0 to stop processing, and some other value to let Windows 
continue to process it by calling user32.DefWindowProcW.

However, the implementation of the event handler handlers (formally "_wnd_proc" 
and "_wnd_proc_view") in pyglet/window/win32/__init__.py never allows event 
processing to stop because user32.DefWindowProcW (which passes the message back 
to Windows) will always be called.  The ambiguity lies in the check on line 618:
https://code.google.com/p/pyglet/source/browse/pyglet/window/win32/__init__.py#6
18
None and 0 will both be evaluated as False effectively, removing the 
distinction between None and 0.  This causes the event to always be processed 
by the event handler, and Windows, which often isn't what we want to happen.  I 
have modified the method so that behaviour is unambiguous:
return None => let windows process the event (call user32.DefWindowProcW)
return all other values => returned to the Window Procedure

Thus, you may create a event handler and omit the return value, which would 
cause the event to continue on being processed, or you may return any other 
value, which then would be passed the the Window Procedure.  I've also noted 
the duplicated code in _wnd_proc and _wnd_proc_view and combined them into a 
closure suitable for generating event handler handlers.

https://code.google.com/p/pyglet/source/browse/pyglet/window/win32/__init__.py#6
18
This line checks for the WM_CLOSE message.  I went through the windows API 
docs, but was unable to find why pyglet needs to treat WM_CLOSE messages 
differently than normal messages.  As is, our handler returns 0, which would 
let make Windows ignore it.  As a result, I have also removed the check for the 
WM_CLOSE message.

There were other event handlers that were implemented sub-optimally, and I have 
changed them to reflect the changes made to the event handler handlers 
(formally "_wnd_proc" and "_wnd_proc_view").

In closing, I tried to keep changes to a minimum and have tested the changes 
and deemed them passing using pyglet's test suite.  In the future, if though to 
be necessary, checks could be made to restrict values passed to the Window 
Procedure to just 0.

Original comment by Leif.The...@gmail.com on 24 Dec 2014 at 8:02

Attachments: