diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index 802372670..e91e4af22 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -33,6 +33,7 @@ endif # CC = llvm-gcc DO_CURSES = -DPLATFORM_NCURSES +# DO_CURSES += -DUSE_GLIBLOOP ifdef CURSES_SMALL_SCREEN DO_CURSES += -DCURSES_SMALL_SCREEN endif diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 337164a69..abb999082 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -397,16 +397,35 @@ curses_util_clearTimer( XW_UtilCtxt* uc, XWTimerReason why ) globals->cGlobals.timerInfo[why].proc = NULL; } +#ifdef USE_GLIBLOOP +static gboolean +onetime_idle( gpointer data ) +{ + LOG_FUNC(); + CursesAppGlobals* globals = (CursesAppGlobals*)data; + if ( server_do( globals->cGlobals.game.server ) ) { + if ( !!globals->cGlobals.game.board ) { + board_draw( globals->cGlobals.game.board ); + } + } + return FALSE; +} +#endif + static void curses_util_requestTime( XW_UtilCtxt* uc ) { + CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; +#ifdef USE_GLIBLOOP + (void)g_idle_add( onetime_idle, globals ); +#else /* I've created a pipe whose read-only end is plugged into the array of fds that my event loop polls so that I can write to it to simulate post-event on a more familiar system. It works, so no complaints! */ - CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; if ( 1 != write( globals->timepipe[1], "!", 1 ) ) { XP_ASSERT(0); } +#endif } /* curses_util_requestTime */ static void @@ -476,7 +495,11 @@ showStatus( CursesAppGlobals* globals ) static XP_Bool handleQuit( CursesAppGlobals* globals ) { +#ifdef USE_GLIBLOOP + g_main_loop_quit( globals->loop ); +#else globals->timeToExit = XP_TRUE; +#endif return XP_TRUE; } /* handleQuit */ @@ -867,8 +890,23 @@ SIGINTTERM_handler( int XP_UNUSED(signal) ) } static void -cursesListenOnSocket( CursesAppGlobals* globals, int newSock ) +cursesListenOnSocket( CursesAppGlobals* globals, int newSock +#ifdef USE_GLIBLOOP + , GIOFunc func +#endif +) { +#ifdef USE_GLIBLOOP + GIOChannel* channel = g_io_channel_unix_new( newSock ); + guint watch = g_io_add_watch( channel, G_IO_IN | G_IO_OUT |G_IO_ERR, + func, globals ); + + SourceData* data = g_malloc( sizeof(*data) ); + data->channel = channel; + data->watch = watch; + globals->sources = g_list_append( globals->sources, data ); + +#else XP_ASSERT( globals->fdCount+1 < FD_MAX ); XP_WARNF( "%s: setting fd[%d] to %d", __func__, globals->fdCount, newSock ); @@ -878,11 +916,27 @@ cursesListenOnSocket( CursesAppGlobals* globals, int newSock ) ++globals->fdCount; XP_LOGF( "%s: there are now %d sources to poll", __func__, globals->fdCount ); +#endif } /* cursesListenOnSocket */ static void curses_stop_listening( CursesAppGlobals* globals, int sock ) { +#ifdef USE_GLIBLOOP + GList* sources = globals->sources; + while ( !!sources ) { + SourceData* data = sources->data; + gint fd = g_io_channel_unix_get_fd( data->channel ); + if ( fd == sock ) { + g_io_channel_unref( data->channel ); + g_free( data ); + globals->sources = g_list_remove_link( globals->sources, sources ); + break; + } + sources = sources->next; + } + +#else int count = globals->fdCount; int i; bool found = false; @@ -897,8 +951,72 @@ curses_stop_listening( CursesAppGlobals* globals, int sock ) assert( found ); --globals->fdCount; +#endif } /* curses_stop_listening */ +#ifdef USE_GLIBLOOP +static gboolean +data_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data ) +{ + if ( 0 != (G_IO_IN & condition) ) { + CursesAppGlobals* globals = (CursesAppGlobals*)data; + int fd = g_io_channel_unix_get_fd( source ); + unsigned char buf[256]; + int nBytes; + CommsAddrRec addrRec; + CommsAddrRec* addrp = NULL; + + /* It's a normal data socket */ + switch ( globals->cGlobals.params->conType ) { +#ifdef XWFEATURE_RELAY + case COMMS_CONN_RELAY: + nBytes = linux_relay_receive( &globals->cGlobals, buf, + sizeof(buf) ); + break; +#endif +#ifdef XWFEATURE_SMS + case COMMS_CONN_SMS: + addrp = &addrRec; + nBytes = linux_sms_receive( &globals->cGlobals, fd, + buf, sizeof(buf), addrp ); + break; +#endif +#ifdef XWFEATURE_BLUETOOTH + case COMMS_CONN_BT: + nBytes = linux_bt_receive( fd, buf, sizeof(buf) ); + break; +#endif + default: + XP_ASSERT( 0 ); /* fired */ + } + + if ( nBytes != -1 ) { + XWStreamCtxt* inboundS; + XP_Bool redraw = XP_FALSE; + + inboundS = stream_from_msgbuf( &globals->cGlobals, buf, nBytes ); + if ( !!inboundS ) { + if ( comms_checkIncomingStream( globals->cGlobals.game.comms, + inboundS, addrp ) ) { + redraw = server_receiveMessage( globals->cGlobals.game.server, + inboundS ); + } + stream_destroy( inboundS ); + } + + /* if there's something to draw resulting from the + message, we need to give the main loop time to reflect + that on the screen before giving the server another + shot. So just call the idle proc. */ + if ( redraw ) { + curses_util_requestTime( globals->cGlobals.params->util ); + } + } + } + return TRUE; +} +#endif + static void curses_socket_changed( void* closure, int oldSock, int newSock, void** XP_UNUSED(storage) ) @@ -908,7 +1026,11 @@ curses_socket_changed( void* closure, int oldSock, int newSock, curses_stop_listening( globals, oldSock ); } if ( newSock != -1 ) { - cursesListenOnSocket( globals, newSock ); + cursesListenOnSocket( globals, newSock +#ifdef USE_GLIBLOOP + , data_socket_proc +#endif + ); } #ifdef XWFEATURE_RELAY @@ -916,6 +1038,21 @@ curses_socket_changed( void* closure, int oldSock, int newSock, #endif } /* curses_socket_changed */ +#ifdef USE_GLIBLOOP +static gboolean +fire_acceptor( GIOChannel* source, GIOCondition condition, gpointer data ) +{ + if ( 0 != (G_IO_IN & condition) ) { + CursesAppGlobals* globals = (CursesAppGlobals*)data; + + int fd = g_io_channel_unix_get_fd( source ); + XP_ASSERT( fd == globals->csInfo.server.serverSocket ); + (*globals->cGlobals.acceptor)( fd, globals ); + } + return TRUE; +} +#endif + static void curses_socket_acceptor( int listener, Acceptor func, CommonGlobals* cGlobals, void** XP_UNUSED(storage) ) @@ -924,9 +1061,14 @@ curses_socket_acceptor( int listener, Acceptor func, CommonGlobals* cGlobals, XP_ASSERT( !cGlobals->acceptor || (func == cGlobals->acceptor) ); cGlobals->acceptor = func; globals->csInfo.server.serverSocket = listener; - cursesListenOnSocket( globals, listener ); + cursesListenOnSocket( globals, listener +#ifdef USE_GLIBLOOP + , fire_acceptor +#endif + ); } +#ifndef USE_GLIBLOOP #ifdef XWFEATURE_RELAY static int figureTimeout( CursesAppGlobals* globals ) @@ -990,10 +1132,12 @@ fireCursesTimer( CursesAppGlobals* globals ) } } /* fireCursesTimer */ #endif +#endif /* * Ok, so this doesn't block yet.... */ +#ifndef USE_GLIBLOOP static XP_Bool blocking_gotEvent( CursesAppGlobals* globals, int* ch ) { @@ -1122,6 +1266,7 @@ blocking_gotEvent( CursesAppGlobals* globals, int* ch ) } return result; } /* blocking_gotEvent */ +#endif static void remapKey( int* kp ) @@ -1438,14 +1583,44 @@ relay_error_curses( void* XP_UNUSED(closure), XWREASON XP_UNUSED_DBG(relayErr) ) #endif } +#ifdef USE_GLIBLOOP +static gboolean +handle_stdin( GIOChannel* source, GIOCondition condition, gpointer data ) +{ + if ( 0 != (G_IO_IN & condition) ) { + gint fd = g_io_channel_unix_get_fd( source ); + XP_ASSERT( 0 == fd ); + CursesAppGlobals* globals = (CursesAppGlobals*)data; + int ch = wgetch( globals->mainWin ); + remapKey( &ch ); + if ( +#ifdef CURSES_SMALL_SCREEN + handleKeyEvent( globals, g_rootMenuListShow, ch ) || +#endif + handleKeyEvent( globals, globals->menuList, ch ) + || handleKeyEvent( globals, g_sharedMenuList, ch ) + || passKeyToBoard( globals, ch ) ) { + if ( g_globals.doDraw ) { + board_draw( globals->cGlobals.game.board ); + globals->doDraw = XP_FALSE; + } + } + } + return TRUE; +} +#endif + void cursesmain( XP_Bool isServer, LaunchParams* params ) { - int piperesult; int width, height; memset( &g_globals, 0, sizeof(g_globals) ); +#ifdef USE_GLIBLOOP + g_globals.loop = g_main_loop_new( NULL, FALSE ); +#endif + g_globals.amServer = isServer; g_globals.cGlobals.params = params; #ifdef XWFEATURE_RELAY @@ -1472,13 +1647,19 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) = params->connInfo.relay.relayName; } #endif + +#ifdef USE_GLIBLOOP + if ( params->quitAfter >= 0 ) { + cursesListenOnSocket( &g_globals, 0, handle_stdin ); /* stdin */ + } +#else cursesListenOnSocket( &g_globals, 0 ); /* stdin */ - piperesult = pipe( g_globals.timepipe ); + int piperesult = pipe( g_globals.timepipe ); XP_ASSERT( piperesult == 0 ); - /* reader pipe */ cursesListenOnSocket( &g_globals, g_globals.timepipe[0] ); +#endif struct sigaction act = { .sa_handler = SIGINTTERM_handler }; sigaction( SIGINT, &act, NULL ); @@ -1595,6 +1776,9 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) drawMenuLargeOrSmall( &g_globals, g_boardMenuList ); board_draw( g_globals.cGlobals.game.board ); +#ifdef USE_GLIBLOOP + g_main_loop_run( g_globals.loop ); +#else while ( !g_globals.timeToExit ) { int ch = 0; if ( blocking_gotEvent( &g_globals, &ch ) ) { @@ -1613,7 +1797,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) } } } - +#endif } if ( !!g_globals.cGlobals.params->fileName ) { XWStreamCtxt* outStream; diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index 62b05506d..e30d7a347 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -65,7 +65,6 @@ struct CursesAppGlobals { WINDOW* menuWin; WINDOW* boardWin; - XP_Bool timeToExit; XP_Bool doDraw; const struct MenuList* menuList; XP_U16 nLinesMenu; @@ -84,14 +83,25 @@ struct CursesAppGlobals { XWGameState state; struct sockaddr_in listenerSockAddr; +#ifdef USE_GLIBLOOP + GMainLoop* loop; + GList* sources; +#else + XP_Bool timeToExit; short fdCount; struct pollfd fdArray[FD_MAX]; /* one for stdio, one for listening socket */ - int timepipe[2]; /* for reading/writing "user events" */ +#endif XP_U32 nextTimer; }; +#ifdef USE_GLIBLOOP +typedef struct _SourceData { + GIOChannel* channel; + gint watch; +} SourceData; +#endif DrawCtx* cursesDrawCtxtMake( WINDOW* boardWin );