|
1 | 1 | import asyncio |
| 2 | +from typing import Union |
| 3 | +import uuid |
2 | 4 |
|
3 | 5 | # ================================================= |
4 | 6 | # do no change import order for *thread* |
|
27 | 29 | # - could be possibly very late |
28 | 30 | # - delay cannot be less than frametime at device refresh rate. |
29 | 31 |
|
| 32 | +# Local testing wrap patch_set_timer in a function and |
| 33 | +# only apply on emscripten platform, so running pygame vanilla doesn't break |
| 34 | +# |
| 35 | +# import platform |
| 36 | +# |
| 37 | +# def patch_timer(): |
| 38 | +# THREADS = {} |
| 39 | +# |
| 40 | +# def patch_set_timer( |
| 41 | +# event: Union[int, pygame.event.Event], millis: int, loops: int = 0): |
| 42 | +# ... |
| 43 | + |
| 44 | +# async def fire_event(thread_uuid): |
| 45 | +# ... |
| 46 | +# |
| 47 | +# pygame.time.set_timer = patch_set_timer |
| 48 | +# |
| 49 | +# if platform.system().lower() == "emscripten": |
| 50 | +# patch_timer() |
| 51 | + |
| 52 | + |
| 53 | +# Global var to keep track of timer threads |
| 54 | +# - key: event type |
| 55 | +# - value: thread uuid |
| 56 | +THREADS = {} |
| 57 | + |
| 58 | + |
| 59 | +def patch_set_timer( |
| 60 | + event: Union[int, pygame.event.Event], millis: int, loops: int = 0): |
| 61 | + """Patches the pygame.time.set_timer function to use gthreads""" |
30 | 62 |
|
31 | | -def patch_set_timer(cust_event_no, millis, loops=0): |
32 | 63 | dlay = float(millis) / 1000 |
33 | | - cevent = pygame.event.Event(cust_event_no) |
34 | | - loop = asyncio.get_event_loop() |
| 64 | + cevent = pygame.event.Event(event) |
| 65 | + event_loop = asyncio.get_event_loop() |
| 66 | + |
| 67 | + async def fire_event(thread_uuid): |
| 68 | + """The thread's target function to handle the timer |
35 | 69 |
|
36 | | - async def fire_event(): |
| 70 | + Early exit conditions: |
| 71 | + - event loop is closed |
| 72 | + - event type is no longer in THREADS dictionary |
| 73 | + - the thread's uuid is not the latest one |
| 74 | + - Max loop iterations if loops param is not zero |
| 75 | + """ |
| 76 | + loop_counter = 0 |
37 | 77 | while True: |
38 | 78 | await asyncio.sleep(dlay) |
39 | | - if loop.is_closed(): |
| 79 | + if ( |
| 80 | + event_loop.is_closed() |
| 81 | + or event not in THREADS |
| 82 | + or THREADS[event] != thread_uuid |
| 83 | + or (loops and loop_counter >= loops) |
| 84 | + ): |
40 | 85 | break |
| 86 | + |
41 | 87 | pygame.event.post(cevent) |
| 88 | + loop_counter += 1 if loops else 0 |
| 89 | + |
| 90 | + if dlay > 0: |
| 91 | + # uuid is used to track the latest thread, |
| 92 | + # stale threads will be terminated |
| 93 | + thread_uuid = uuid.uuid4() |
| 94 | + Thread(target=fire_event, args=[thread_uuid]).start() |
| 95 | + THREADS[event] = thread_uuid |
42 | 96 |
|
43 | | - Thread(target=fire_event).start() |
| 97 | + else: |
| 98 | + # This cancels the timer for the event |
| 99 | + if event in THREADS: |
| 100 | + del THREADS[event] |
44 | 101 |
|
45 | 102 |
|
46 | 103 | pygame.time.set_timer = patch_set_timer |
0 commit comments