rtt-f030/bsp/stm32_radio/player_ui.c

794 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <rtgui/rtgui.h>
#include <rtgui/image.h>
#include <rtgui/rtgui_system.h>
#include <rtgui/widgets/view.h>
#include <rtgui/widgets/list_view.h>
#include <rtgui/widgets/workbench.h>
#include <rtgui/widgets/filelist_view.h>
#include <string.h>
#include <dfs_posix.h>
#include "mp3.h"
#include "picture.h"
#include "player_ui.h"
#include "player_bg.h"
#include "play_list.h"
#include "station_list.h"
#include "play.hdh"
#include "stop.hdh"
#define RADIO_FN "/radio.pls"
static struct rtgui_view* home_view;
static struct rtgui_list_view* function_view;
static struct rtgui_workbench* workbench;
static rtgui_timer_t* info_timer;
static rt_thread_t player_ui_tid = RT_NULL;
static enum PLAYER_MODE player_mode = PLAYER_STOP;
static enum PLAYER_STEP next_step = PLAYER_STEP_STOP;
static struct tag_info tinfo;
void player_play_file(const char* fn);
void player_play_url(const char* url);
static void info_timer_timeout(rtgui_timer_t* timer, void* parameter)
{
struct rtgui_dc* dc;
rtgui_color_t saved;
dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(home_view));
if (dc == RT_NULL) return ;
saved = RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view));
RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view)) = RTGUI_RGB(206, 231, 255);
rtgui_dc_draw_hline(dc, 14, 14 + (tinfo.position * 212)/ tinfo.duration,
75);
if ((player_mode == PLAYER_PLAY_RADIO) && ((tinfo.position * 212 + 14)/tinfo.duration) < 226)
{
RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view)) = RTGUI_RGB(82, 199, 16);
rtgui_dc_draw_hline(dc, 14 + (tinfo.position * 212)/ tinfo.duration, 226, 75);
}
RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view)) = saved;
rtgui_dc_end_drawing(dc);
}
static void player_update_tag_info(struct rtgui_dc* dc)
{
rtgui_rect_t rect;
char line[32];
rtgui_color_t saved;
rtgui_image_t *background;
saved = rtgui_dc_get_color(dc);
rtgui_dc_set_color(dc, black);
rect.x1 = 0; rect.y1 = 0;
rect.x2 = 240; rect.y2 = 65;
/* draw background */
background = rtgui_image_create_from_file("hdc",
"/resource/bg.hdc", RT_FALSE);
if (background != RT_NULL)
{
rtgui_image_blit(background, dc, &rect);
rtgui_image_destroy(background);
background = RT_NULL;
}
else
{
rtgui_dc_fill_rect(dc, &rect);
}
/* draw playing information */
rect.x1 = 28; rect.y1 = 12;
rect.x2 = 220; rect.y2 = rect.y1 + 16;
if (player_mode == PLAYER_STOP)
{
rt_snprintf(line, sizeof(line),
"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
rtgui_dc_draw_text(dc, line, &rect);
}
else
rtgui_dc_draw_text(dc, tinfo.title, &rect);
rect.x1 = 28; rect.y1 = 39;
rect.x2 = 220; rect.y2 = 59;
if (player_mode == PLAYER_STOP)
{
rt_snprintf(line, sizeof(line),
"radio.rt-thread.org");
rtgui_dc_draw_text(dc, line, &rect);
}
else
rtgui_dc_draw_text(dc, tinfo.artist, &rect);
if ((tinfo.duration != 0) && player_mode == PLAYER_PLAY_FILE)
{
rect.x1 = rect.x2 - 64;
rt_snprintf(line, sizeof(line), "%02d:%02d:%02d",
tinfo.duration / 3600, tinfo.duration / 60, tinfo.duration % 60);
rtgui_dc_draw_text(dc, line, &rect);
}
rtgui_dc_set_color(dc, saved);
}
static rt_uint32_t read_line(int fd, char* line, rt_uint32_t line_size)
{
char *pos, *next;
rt_uint32_t length;
length = read(fd, line, line_size);
if (length > 0)
{
pos = strstr(line, "\r\n");
if (pos == RT_NULL)
{
pos = strstr(line, "\n");
next = pos ++;
}
else next = pos + 2;
if (pos != RT_NULL)
{
*pos = '\0';
/* move back */
lseek(fd, -(length - (next - line)), SEEK_CUR);
length = pos - line;
}
else length = 0;
}
return length;
}
static void function_play_radio(void* parameter)
{
struct station_list* list;
struct station_item* item;
list = station_list_create(RADIO_FN);
if (list != RT_NULL)
{
item = station_list_select(list, workbench);
if (item != RT_NULL)
{
player_play_url(item->url);
}
station_list_destroy(list);
}
}
static void function_filelist(void* parameter)
{
rtgui_rect_t rect;
rtgui_filelist_view_t *view;
rtgui_widget_get_rect(RTGUI_WIDGET(workbench), &rect);
view = rtgui_filelist_view_create(workbench, "/", "*.*", &rect);
if (view != RT_NULL)
{
if (rtgui_view_show(RTGUI_VIEW(view), RT_TRUE) == RTGUI_MODAL_OK)
{
char fn[64];
/* get open file */
rt_snprintf(fn, 64, "%s/%s", view->current_directory,
view->items[view->current_item].name);
if (strstr(view->items[view->current_item].name , ".mp3") != RT_NULL ||
strstr(view->items[view->current_item].name , ".MP3") != RT_NULL ||
strstr(view->items[view->current_item].name , ".wav") != RT_NULL ||
strstr(view->items[view->current_item].name , ".WAV") != RT_NULL)
{
/* clear old play list */
play_list_clear();
play_list_append(fn);
player_mode = PLAYER_PLAY_FILE;
next_step = PLAYER_STEP_STOP;
player_play_file(play_list_start());
}
else if (strstr(view->items[view->current_item].name , ".m3u") != RT_NULL ||
strstr(view->items[view->current_item].name , ".M3U") != RT_NULL)
{
/* read all of music filename to a list */
int fd;
char line[64];
fd = open(fn, O_RDONLY, 0);
if (fd >= 0)
{
rt_uint32_t length;
length = read_line(fd, line, sizeof(line));
/* clear old play list */
play_list_clear();
do
{
length = read_line(fd, line, sizeof(line));
if (length > 0)
{
if (strstr(line, "http:") != RT_NULL)
{
play_list_append(line);
}
else if (line[0] != '/')
{
rt_snprintf(fn, sizeof(fn),
"%s/%s", view->current_directory, line);
play_list_append(fn);
}
else play_list_append(line);
}
} while (length > 0);
close(fd);
if (play_list_items() > 0)
{
player_mode = PLAYER_PLAY_FILE;
next_step = PLAYER_STEP_NEXT;
player_play_file(play_list_start());
}
}
}
}
/* destroy view */
rtgui_filelist_view_destroy(view);
}
return;
}
static void function_device(void* parameter)
{
rtgui_view_t *view;
extern rtgui_view_t* device_view_create(rtgui_workbench_t* workbench);
view = device_view_create(workbench);
if (view != RT_NULL)
{
rtgui_view_show(view, RT_FALSE);
}
return;
}
static void function_player(void* parameter)
{
rtgui_view_show(home_view, RT_FALSE);
return;
}
static void function_show_picure(void* parameter)
{
rtgui_view_t *view;
view = picture_view_create(workbench);
if (view != RT_NULL)
{
rtgui_view_show(view, RT_TRUE);
rtgui_view_destroy(view);
}
return;
}
void function_action(void* parameter)
{
rt_kprintf("item action!\n");
return;
}
void function_cable(void* parameter)
{
extern void USB_cable(void);
USB_cable();
}
const struct rtgui_list_item function_list[] =
{
{"ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨", RT_NULL, function_play_radio, RT_NULL},
{"<EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>̨", RT_NULL, function_action, RT_NULL},
{"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>", RT_NULL, function_filelist, RT_NULL},
{"<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼƬ", RT_NULL, function_show_picure, RT_NULL},
{"<EFBFBD><EFBFBD><EFBFBD>Ϣ", RT_NULL, function_device, RT_NULL},
{"ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>", RT_NULL, function_action, RT_NULL},
{"USB <20><><EFBFBD><EFBFBD>", RT_NULL, function_cable, RT_NULL},
{"<EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>", RT_NULL, function_player, RT_NULL},
};
void player_set_position(rt_uint32_t position)
{
if (player_mode != PLAYER_PLAY_RADIO)
{
tinfo.position = position / (tinfo.bit_rate / 8);
}
else
{
tinfo.position = position;
}
}
void player_set_title(const char* title)
{
strncpy(tinfo.title, title, 40);
}
void player_set_buffer_status(rt_bool_t buffering)
{
if (buffering == RT_TRUE)
strncpy(tinfo.artist, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...", 40);
else
strncpy(tinfo.artist, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...", 40);
}
void player_play_file(const char* fn)
{
struct rtgui_dc* dc;
rtgui_color_t saved;
rt_bool_t is_mp3;
is_mp3 = RT_FALSE;
if (strstr(fn, ".mp3") != RT_NULL ||
strstr(fn, ".MP3") != RT_NULL)
is_mp3 = RT_TRUE;
else if (strstr(fn, ".wav") != RT_NULL ||
strstr(fn, ".wav") != RT_NULL)
is_mp3 = RT_FALSE;
else return; /* not supported audio format */
if (is_mp3 == RT_TRUE)
{
/* get music tag information */
mp3_get_info(fn, &tinfo);
if (tinfo.title[0] == '\0')
rt_snprintf(tinfo.title, sizeof(tinfo.title), "<δ֪<CEB4><D6AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>");
}
else
{
/* wav file */
rt_snprintf(tinfo.title, sizeof(tinfo.title), "<δ֪<CEB4><D6AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>>");
rt_snprintf(tinfo.artist, sizeof(tinfo.title), "<wav<61><76><EFBFBD><EFBFBD>>");
tinfo.duration = 0;
}
/* set player mode */
player_mode = PLAYER_PLAY_FILE;
dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(home_view));
if (dc != RT_NULL)
{
rtgui_rect_t play_rect;
rtgui_image_t *button;
/* update tag information */
player_update_tag_info(dc);
/* reset progress bar */
saved = RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view));
RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view)) = RTGUI_RGB(82, 199, 16);
rtgui_dc_draw_hline(dc, 14, 226, 75);
RTGUI_WIDGET_FOREGROUND(RTGUI_WIDGET(home_view)) = saved;
/* update play button */
button = rtgui_image_create_from_mem("hdc",
play_hdh, sizeof(play_hdh), RT_FALSE);
play_rect.x1 = 32; play_rect.y1 = 92;
play_rect.x2 = 61; play_rect.y2 = 114;
rtgui_image_blit(button, dc, &play_rect);
rtgui_image_destroy(button);
rtgui_dc_end_drawing(dc);
}
rtgui_view_show(home_view, RT_FALSE);
player_play_req(fn);
}
void player_play_url(const char* url)
{
struct rtgui_dc* dc;
/* set music tag information */
strncpy(tinfo.title, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨", 40);
player_set_buffer_status(RT_TRUE);
tinfo.duration = 320 * 1024; /* 320 k */
/* set player mode */
player_mode = PLAYER_PLAY_RADIO;
dc = rtgui_dc_begin_drawing(RTGUI_WIDGET(home_view));
if (dc != RT_NULL)
{
rtgui_rect_t play_rect;
rtgui_image_t *button;
/* update tag information */
player_update_tag_info(dc);
/* update play button */
button = rtgui_image_create_from_mem("hdc",
play_hdh, sizeof(play_hdh), RT_FALSE);
play_rect.x1 = 32; play_rect.y1 = 92;
play_rect.x2 = 61; play_rect.y2 = 114;
rtgui_image_blit(button, dc, &play_rect);
rtgui_image_destroy(button);
rtgui_dc_end_drawing(dc);
}
rtgui_view_show(home_view, RT_FALSE);
player_play_req(url);
}
static rt_bool_t home_view_event_handler(struct rtgui_widget* widget, struct rtgui_event* event)
{
if (event->type == RTGUI_EVENT_PAINT)
{
struct rtgui_dc* dc;
struct rtgui_rect rect;
rtgui_color_t saved;
rtgui_image_t *background;
dc = rtgui_dc_begin_drawing(widget);
if (dc == RT_NULL) return RT_FALSE;
rtgui_widget_get_rect(widget, &rect);
saved = RTGUI_WIDGET_FOREGROUND(widget);
/* draw background */
background = rtgui_image_create_from_file("hdc",
"/resource/bg.hdc", RT_FALSE);
if (background != RT_NULL)
{
rtgui_image_t *play;
rtgui_rect_t play_rect;
rtgui_image_blit(background, dc, &rect);
rtgui_image_destroy(background);
background = RT_NULL;
if (player_mode == PLAYER_STOP)
play = rtgui_image_create_from_mem("hdc",
stop_hdh, sizeof(stop_hdh), RT_FALSE);
else
play = rtgui_image_create_from_mem("hdc",
play_hdh, sizeof(play_hdh), RT_FALSE);
play_rect.x1 = 32; play_rect.y1 = 92;
play_rect.x2 = 61; play_rect.y2 = 114;
rtgui_image_blit(play, dc, &play_rect);
rtgui_image_destroy(play);
}
else
{
rtgui_dc_fill_rect(dc, &rect);
}
/* draw playing information */
rtgui_dc_set_color(dc, black);
{
char line[32];
rect.x1 = 28; rect.y1 = 12;
rect.x2 = 220; rect.y2 = rect.y1 + 16;
if (player_mode == PLAYER_STOP)
{
rt_snprintf(line, sizeof(line),
"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
rtgui_dc_draw_text(dc, line, &rect);
}
else
rtgui_dc_draw_text(dc, tinfo.title, &rect);
rect.x1 = 28; rect.y1 = 39;
rect.x2 = 220; rect.y2 = 59;
if (player_mode == PLAYER_STOP)
{
rt_snprintf(line, sizeof(line),
"radio.rt-thread.org");
rtgui_dc_draw_text(dc, line, &rect);
}
else
rtgui_dc_draw_text(dc, tinfo.artist, &rect);
if ((tinfo.duration != 0) && (player_mode == PLAYER_PLAY_FILE))
{
rect.x1 = rect.x2 - 64;
rt_snprintf(line, sizeof(line), "%02d:%02d:%02d",
tinfo.duration / 3600, tinfo.duration / 60, tinfo.duration % 60);
rtgui_dc_draw_text(dc, line, &rect);
}
}
RTGUI_WIDGET_FOREGROUND(widget) = RTGUI_RGB(82, 199, 16);
rtgui_dc_draw_hline(dc, 14, 226, 75);
RTGUI_WIDGET_FOREGROUND(widget) = saved;
if (player_mode == PLAYER_PLAY_FILE)
{
char line[32];
rt_uint32_t index;
struct play_item* item;
rect.x1 = 20; rect.y1 = 150;
rect.x2 = 220; rect.y2 = 168;
for (index = 0; index < play_list_items() && index < 8; index ++)
{
item = play_list_item(index);
rtgui_dc_draw_text(dc, item->title, &rect);
rect.x1 = rect.x2 - 64;
rt_snprintf(line, sizeof(line), "%02d:%02d:%02d",
item->duration / 3600,
item->duration / 60,
item->duration % 60);
rtgui_dc_draw_text(dc, line, &rect);
/* move to next item */
rect.x1 = 20;
rect.y1 += 18; rect.y2 += 18;
}
}
rtgui_dc_end_drawing(dc);
return RT_FALSE;
}
else if (event->type == RTGUI_EVENT_KBD)
{
struct rtgui_event_kbd* ekbd = (struct rtgui_event_kbd*)event;
if (ekbd->type == RTGUI_KEYDOWN)
{
switch (ekbd->key)
{
case RTGUIK_RIGHT:
if (player_mode == PLAYER_PLAY_FILE && play_list_items() > 0)
{
player_stop_req();
next_step = PLAYER_STEP_NEXT;
}
break;
case RTGUIK_LEFT:
if (player_mode == PLAYER_PLAY_FILE && play_list_items() > 0)
{
player_stop_req();
next_step = PLAYER_STEP_PREV;
}
break;
case RTGUIK_RETURN:
if (player_is_playing() == RT_TRUE)
{
player_stop_req();
next_step = PLAYER_STEP_STOP;
}
else
{
if ((player_mode == PLAYER_STOP) && (play_list_items() > 0))
{
next_step = PLAYER_STEP_NEXT;
player_play_file(play_list_current_item());
}
}
break;
case RTGUIK_DOWN:
rtgui_view_show(RTGUI_VIEW(function_view), RT_FALSE);
break;
}
}
return RT_FALSE;
}
else if (event->type == RTGUI_EVENT_COMMAND)
{
struct rtgui_event_command* ecmd = (struct rtgui_event_command*)event;
switch (ecmd->command_id)
{
case PLAYER_REQUEST_PLAY_SINGLE_FILE:
case PLAYER_REQUEST_PLAY_LIST:
rtgui_timer_start(info_timer);
break;
case PLAYER_REQUEST_STOP:
{
rtgui_timer_stop(info_timer);
switch (next_step)
{
case PLAYER_STEP_STOP:
{
struct rtgui_dc* dc;
rtgui_color_t saved;
rtgui_image_t *button;
rtgui_rect_t play_rect;
player_mode = PLAYER_STOP;
dc = rtgui_dc_begin_drawing(widget);
if (dc == RT_NULL) return RT_FALSE;
player_update_tag_info(dc);
saved = RTGUI_WIDGET_FOREGROUND(widget);
RTGUI_WIDGET_FOREGROUND(widget) = RTGUI_RGB(82, 199, 16);
rtgui_dc_draw_hline(dc, 14, 226, 75);
/* update play button */
button = rtgui_image_create_from_mem("hdc",
stop_hdh, sizeof(stop_hdh), RT_FALSE);
play_rect.x1 = 32; play_rect.y1 = 92;
play_rect.x2 = 61; play_rect.y2 = 114;
rtgui_image_blit(button, dc, &play_rect);
rtgui_image_destroy(button);
RTGUI_WIDGET_FOREGROUND(widget) = saved;
rtgui_dc_end_drawing(dc);
}
break;
case PLAYER_STEP_NEXT:
if (play_list_is_end() == RT_TRUE)
{
struct rtgui_dc* dc;
rtgui_color_t saved;
rtgui_image_t *button;
rtgui_rect_t play_rect;
/* set stat */
next_step = PLAYER_STEP_STOP;
player_mode = PLAYER_STOP;
/* update UI */
dc = rtgui_dc_begin_drawing(widget);
if (dc == RT_NULL) return RT_FALSE;
player_update_tag_info(dc);
saved = RTGUI_WIDGET_FOREGROUND(widget);
RTGUI_WIDGET_FOREGROUND(widget) = RTGUI_RGB(82, 199, 16);
rtgui_dc_draw_hline(dc, 14, 226, 75);
/* update play button */
button = rtgui_image_create_from_mem("hdc",
stop_hdh, sizeof(stop_hdh), RT_FALSE);
play_rect.x1 = 32; play_rect.y1 = 92;
play_rect.x2 = 61; play_rect.y2 = 114;
rtgui_image_blit(button, dc, &play_rect);
rtgui_image_destroy(button);
RTGUI_WIDGET_FOREGROUND(widget) = saved;
rtgui_dc_end_drawing(dc);
}
else
{
player_play_file(play_list_next());
next_step = PLAYER_STEP_NEXT;
}
break;
case PLAYER_STEP_PREV:
player_play_file(play_list_prev());
next_step = PLAYER_STEP_NEXT;
break;
};
}
break;
default:
break;
}
return RT_FALSE;
}
return rtgui_view_event_handler(widget, event);
}
rt_bool_t player_workbench_event_handler(rtgui_widget_t *widget, rtgui_event_t *event)
{
if (event->type == RTGUI_EVENT_KBD)
{
struct rtgui_event_kbd* ekbd = (struct rtgui_event_kbd*)event;
if ((ekbd->type == RTGUI_KEYUP) && ekbd->key == RTGUIK_HOME)
{
/* active home view */
if (workbench->current_view != home_view)
{
rtgui_view_show(home_view, RT_FALSE);
return RT_FALSE;
}
}
}
return rtgui_workbench_event_handler(widget, event);
}
static void player_entry(void* parameter)
{
rt_mq_t mq;
rtgui_rect_t rect;
mq = rt_mq_create("ply_ui", 256, 4, RT_IPC_FLAG_FIFO);
rtgui_thread_register(rt_thread_self(), mq);
/* create information timer */
info_timer = rtgui_timer_create(RT_TICK_PER_SECOND,
RT_TIMER_FLAG_PERIODIC,
info_timer_timeout, RT_NULL);
workbench = rtgui_workbench_create("main", "workbench");
if (workbench == RT_NULL) return;
rtgui_widget_set_event_handler(RTGUI_WIDGET(workbench), player_workbench_event_handler);
/* add home view */
home_view = rtgui_view_create("Home");
rtgui_widget_set_event_handler(RTGUI_WIDGET(home_view), home_view_event_handler);
rtgui_workbench_add_view(workbench, home_view);
/* this view can be focused */
RTGUI_WIDGET(home_view)->flag |= RTGUI_WIDGET_FLAG_FOCUSABLE;
/* set widget focus */
rtgui_widget_focus(RTGUI_WIDGET(home_view));
rtgui_view_show(home_view, RT_FALSE);
/* add function view */
rtgui_widget_get_rect(RTGUI_WIDGET(workbench), &rect);
function_view = rtgui_list_view_create(function_list,
sizeof(function_list)/sizeof(struct rtgui_list_item),
&rect);
rtgui_workbench_add_view(workbench, RTGUI_VIEW(function_view));
rtgui_workbench_event_loop(workbench);
rtgui_thread_deregister(rt_thread_self());
rt_mq_delete(mq);
}
void player_notify_play(void)
{
struct rtgui_event_command ecmd;
RTGUI_EVENT_COMMAND_INIT(&ecmd);
ecmd.type = RTGUI_CMD_USER_INT;
ecmd.command_id = PLAYER_REQUEST_PLAY_SINGLE_FILE;
rtgui_thread_send(player_ui_tid, &ecmd.parent, sizeof(ecmd));
}
void player_notify_stop()
{
struct rtgui_event_command ecmd;
RTGUI_EVENT_COMMAND_INIT(&ecmd);
ecmd.type = RTGUI_CMD_USER_INT;
ecmd.command_id = PLAYER_REQUEST_STOP;
rtgui_thread_send(player_ui_tid, &ecmd.parent, sizeof(ecmd));
}
void player_ui_init()
{
player_ui_tid = rt_thread_create("ply_ui", player_entry, RT_NULL,
0x800, 25, 5);
if (player_ui_tid != RT_NULL)
rt_thread_startup(player_ui_tid);
}