| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi_simple/ps_event.h" |
| |
| #include <assert.h> |
| #include <pthread.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "ppapi_simple/ps_instance.h" |
| #include "ppapi_simple/ps_interface.h" |
| |
| #define NO_BLOCK 0 |
| #define BLOCK 1 |
| |
| struct PSMessageHandlerInfo { |
| char* message_name; |
| PSMessageHandler_t func; |
| void* user_data; |
| struct PSMessageHandlerInfo* prev; |
| struct PSMessageHandlerInfo* next; |
| }; |
| |
| static uint32_t s_events_enabled = PSE_NONE; |
| static struct PSEvent* s_event_head; |
| static struct PSEvent* s_event_tail; |
| static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_cond_t s_cond = PTHREAD_COND_INITIALIZER; |
| static struct PSMessageHandlerInfo* s_handler_head; |
| static struct PSMessageHandlerInfo* s_handler_tail; |
| |
| static struct PSMessageHandlerInfo* FindMessageHandler( |
| const char* message_name) { |
| struct PSMessageHandlerInfo* info = s_handler_head; |
| while (info) { |
| if (strcmp(info->message_name, message_name) == 0) { |
| return info; |
| } |
| |
| info = info->next; |
| } |
| |
| return NULL; |
| } |
| |
| static void EnqueueEvent(struct PSEvent* event) { |
| pthread_mutex_lock(&s_lock); |
| |
| if (!s_event_tail) { |
| s_event_head = s_event_tail = event; |
| event->next = NULL; |
| } else { |
| s_event_tail->next = event; |
| s_event_tail = event; |
| } |
| |
| pthread_cond_signal(&s_cond); |
| pthread_mutex_unlock(&s_lock); |
| } |
| |
| static struct PSEvent* DequeueEvent(int block) { |
| pthread_mutex_lock(&s_lock); |
| |
| if (block) { |
| while (s_event_head == NULL) { |
| pthread_cond_wait(&s_cond, &s_lock); |
| } |
| } |
| |
| if (s_event_head == NULL) { |
| pthread_mutex_unlock(&s_lock); |
| return NULL; |
| } |
| |
| struct PSEvent* item = s_event_head; |
| |
| if (s_event_head == s_event_tail) { |
| s_event_head = s_event_tail = NULL; |
| } else { |
| s_event_head = s_event_head->next; |
| } |
| |
| pthread_mutex_unlock(&s_lock); |
| |
| return item; |
| } |
| |
| struct PSEvent* PSEventTryAcquire() { |
| struct PSEvent* event; |
| while (1) { |
| event = DequeueEvent(NO_BLOCK); |
| if (NULL == event) |
| break; |
| if (s_events_enabled & event->type) |
| break; |
| /* Release filtered events & continue to acquire. */ |
| PSEventRelease(event); |
| } |
| return event; |
| } |
| |
| struct PSEvent* PSEventWaitAcquire() { |
| struct PSEvent* event; |
| while (1) { |
| event = DequeueEvent(BLOCK); |
| if (s_events_enabled & event->type) |
| break; |
| /* Release filtered events & continue to acquire. */ |
| PSEventRelease(event); |
| } |
| return event; |
| } |
| |
| void PSEventRelease(struct PSEvent* event) { |
| if (event) { |
| switch (event->type) { |
| case PSE_INSTANCE_HANDLEMESSAGE: |
| PSInterfaceVar()->Release(event->as_var); |
| break; |
| case PSE_INSTANCE_HANDLEINPUT: |
| case PSE_INSTANCE_DIDCHANGEVIEW: |
| if (event->as_resource) { |
| PSInterfaceCore()->ReleaseResource(event->as_resource); |
| } |
| break; |
| default: |
| break; |
| } |
| free(event); |
| } |
| } |
| |
| void PSEventSetFilter(PSEventTypeMask filter) { |
| s_events_enabled = filter; |
| if (filter == 0) { |
| static int s_warn_once = 1; |
| if (s_warn_once) { |
| PSInstanceWarn( |
| "PSInstance::SetEnabledEvents(mask) where mask == 0 will block\n"); |
| PSInstanceWarn( |
| "all events. This can come from PSEventSetFilter(PSE_NONE);\n"); |
| s_warn_once = 0; |
| } |
| } |
| } |
| |
| void PSEventPost(PSEventType type) { |
| assert(PSE_GRAPHICS3D_GRAPHICS3DCONTEXTLOST == type || |
| PSE_MOUSELOCK_MOUSELOCKLOST == type); |
| |
| struct PSEvent* event = malloc(sizeof(struct PSEvent)); |
| memset(event, 0, sizeof(*event)); |
| event->type = type; |
| EnqueueEvent(event); |
| } |
| |
| void PSEventPostBool(PSEventType type, PP_Bool bool_value) { |
| assert(PSE_INSTANCE_DIDCHANGEFOCUS == type); |
| |
| struct PSEvent* event = malloc(sizeof(struct PSEvent)); |
| memset(event, 0, sizeof(*event)); |
| event->type = type; |
| event->as_bool = bool_value; |
| EnqueueEvent(event); |
| } |
| |
| void PSEventPostVar(PSEventType type, struct PP_Var var) { |
| assert(PSE_INSTANCE_HANDLEMESSAGE == type); |
| |
| /* If the message is a dictionary then see if it matches one of the specific |
| * handlers, then call that handler rather than queuing an event. */ |
| if (var.type == PP_VARTYPE_DICTIONARY) { |
| struct PP_Var keys_var = PSInterfaceVarDictionary()->GetKeys(var); |
| if (PSInterfaceVarArray()->GetLength(keys_var) == 1) { |
| struct PP_Var key_var = PSInterfaceVarArray()->Get(keys_var, 0); |
| uint32_t key_len; |
| const char* key_str = PSInterfaceVar()->VarToUtf8(key_var, &key_len); |
| char* key_cstr = alloca(key_len + 1); |
| memcpy(key_cstr, key_str, key_len); |
| key_cstr[key_len] = 0; |
| PSInstanceTrace("calling handler for: %s\n", key_cstr); |
| |
| struct PSMessageHandlerInfo* handler_info = FindMessageHandler(key_cstr); |
| if (handler_info) { |
| struct PP_Var value_var = PSInterfaceVarDictionary()->Get(var, key_var); |
| handler_info->func(key_var, value_var, handler_info->user_data); |
| PSInterfaceVar()->Release(value_var); |
| PSInterfaceVar()->Release(key_var); |
| PSInterfaceVar()->Release(keys_var); |
| return; |
| } |
| |
| PSInterfaceVar()->Release(key_var); |
| } |
| |
| PSInterfaceVar()->Release(keys_var); |
| } |
| |
| PSInterfaceVar()->AddRef(var); |
| struct PSEvent *env = malloc(sizeof(struct PSEvent)); |
| memset(env, 0, sizeof(*env)); |
| env->type = type; |
| env->as_var = var; |
| EnqueueEvent(env); |
| } |
| |
| void PSEventPostResource(PSEventType type, PP_Resource resource) { |
| assert(PSE_INSTANCE_HANDLEINPUT == type || |
| PSE_INSTANCE_DIDCHANGEVIEW == type); |
| |
| if (resource) { |
| PSInterfaceCore()->AddRefResource(resource); |
| } |
| struct PSEvent* event = malloc(sizeof(struct PSEvent)); |
| memset(event, 0, sizeof(*event)); |
| event->type = type; |
| event->as_resource = resource; |
| EnqueueEvent(event); |
| } |
| |
| void PSEventRegisterMessageHandler(const char* message_name, |
| PSMessageHandler_t func, |
| void* user_data) { |
| PSInstanceTrace("registering msg handler: %s\n", message_name); |
| struct PSMessageHandlerInfo* handler = FindMessageHandler(message_name); |
| |
| if (func == NULL) { |
| /* Unregister handler, if it exists */ |
| if (handler) { |
| if (handler->prev) { |
| handler->prev->next = handler->next; |
| } else { |
| s_handler_head = handler->next; |
| } |
| |
| if (handler->next) { |
| handler->next->prev = handler->prev; |
| } else { |
| s_handler_tail = handler->prev; |
| } |
| |
| free(handler->message_name); |
| free(handler); |
| } |
| return; |
| } |
| |
| if (handler) { |
| /* Already registered, change its function */ |
| handler->func = func; |
| handler->user_data = user_data; |
| } else { |
| /* Not registered, append a new handler info */ |
| struct PSMessageHandlerInfo* handler_info = |
| malloc(sizeof(struct PSMessageHandlerInfo)); |
| handler_info->message_name = strdup(message_name); |
| handler_info->func = func; |
| handler_info->user_data = user_data; |
| handler_info->next = NULL; |
| handler_info->prev = s_handler_tail; |
| |
| if (s_handler_tail) { |
| s_handler_tail->next = handler_info; |
| s_handler_tail = handler_info; |
| } else { |
| s_handler_head = s_handler_tail = handler_info; |
| } |
| } |
| } |