// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // $Log:$ // // DESCRIPTION: // DOOM graphics stuff for X11, UNIX. // //----------------------------------------------------------------------------- #include #include #include #include #include #include "../doomstat.h" #include "../i_system.h" #include "../v_video.h" #include "../m_argv.h" #include "../d_main.h" #include "../doomdef.h" #include "../ib_video.h" #include #include #include #include #include #include typedef struct DoomContext DoomContext; #define DEF_DOOM_STATE_FUNC(fn) int fn(DoomContext *ctx) typedef DEF_DOOM_STATE_FUNC(DoomStateFunc); struct DoomContext { nng_socket pub; nng_socket sub; doomid_t id; DP_DoomState state; DoomStateFunc *f; }; static int publish_state(DoomContext *ctx) { nng_msg *msg = NULL; int res = 0; if ((res = nng_msg_alloc(&msg, sizeof(MsgDoomState)))) dp_nng_fatal("doom/nng_msg_alloc", res); MsgDoomState *dmsg = DP_NNG_BODY_AS(msg, MsgDoomState); dmsg->head.msgType = DP_MT_DoomState; dmsg->doomId = ctx->id; dmsg->doomState = ctx->state; res = nng_sendmsg(ctx->pub, msg, 0); return res; } DEF_DOOM_STATE_FUNC(do_doom_ready); DEF_DOOM_STATE_FUNC(do_doom_running); DEF_DOOM_STATE_FUNC(do_doom_ready) { assert(ctx->state == DP_DS_Ready); int res = 0; if ((res = publish_state(ctx))) dp_nng_fatal("doom/publish_sate", res); nng_msg *msg = NULL; if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg))) { if (!dp_nng_is_timeout(res)) dp_nng_fatal("doom/recvmsg", res); return 0; } log_trace("do_doom_ready received message of size %zu", nng_msg_len(msg)); MessageBase *msgBase = DP_NNG_BODY_AS(msg, MessageBase); DP_DoomCommand cmd = DP_DC_Noop; if (msgBase->msgType == DP_MT_Command) { MsgCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgCommand); if (msgCmd->doomId == ctx->id) cmd = msgCmd->cmd; } else if (msgBase->msgType == DP_MT_McstCommand) { MsgMcstCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgMcstCommand); cmd = msgCmd->cmd; } if (cmd == DP_DC_RunDoom) { ctx->state = DP_DS_Running; ctx->f = do_doom_running; if ((res = publish_state(ctx))) dp_nng_fatal("doom/publish_sate", res); } else if (cmd == DP_DC_Endoom) { ctx->state = DP_DS_Endoom; if ((res = publish_state(ctx))) dp_nng_fatal("doom/publish_sate", res); } nng_msg_free(msg); return res; } DEF_DOOM_STATE_FUNC(do_doom_running) { assert(ctx->state == DP_DS_Running); // Non-blocking receive of incoming messages. // Check if we should quit. // Handle incoming input. // Let doom render a new frame. // Publish the frame. int res = 0; nng_msg *msg = NULL; if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg))) { if (!dp_nng_is_timeout(res)) dp_nng_fatal("doom/recvmsg", res); return 0; } log_trace("do_doom_running received message of size %zu", nng_msg_len(msg)); MessageBase *msgBase = DP_NNG_BODY_AS(msg, MessageBase); DP_DoomCommand cmd = DP_DC_Noop; if (msgBase->msgType == DP_MT_Command) { MsgCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgCommand); if (msgCmd->doomId == ctx->id) cmd = msgCmd->cmd; } else if (msgBase->msgType == DP_MT_McstCommand) { MsgMcstCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgMcstCommand); cmd = msgCmd->cmd; } if (cmd == DP_DC_Endoom) { ctx->state = DP_DS_Endoom; if ((res = publish_state(ctx))) dp_nng_fatal("doom/publish_sate", res); } nng_msg_free(msg); return res; } static size_t g_frameBufferSize; static uint8_t *g_frameBuffer; static size_t g_pitch; DoomContext g_doomContext; // // IB_StartTic // void IB_StartTic (void) { /* TODO: post input events (could this be done in IB_FinishUpdate? try it) case SDL_KEYDOWN: event.type = ev_keydown; event.data1 = xlatekey(sdl_event.key.keysym.sym); D_PostEvent(&event); */ } void IB_GetFramebuffer(unsigned char **pixels, size_t *pitch) { *pixels = g_frameBuffer; *pitch = g_pitch; } void IB_FinishUpdate (void) { DoomContext *ctx = &g_doomContext; assert(ctx->state == DP_DS_Running); { int res = 0; nng_msg *msg = NULL; if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg))) { if (!dp_nng_is_timeout(res)) dp_nng_fatal("doom/recvmsg", res); } else { MessageBase *msgBase = DP_NNG_BODY_AS(msg, MessageBase); DP_DoomCommand cmd = DP_DC_Noop; if (msgBase->msgType == DP_MT_Command) { MsgCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgCommand); if (msgCmd->doomId == ctx->id) cmd = msgCmd->cmd; } else if (msgBase->msgType == DP_MT_McstCommand) { MsgMcstCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgMcstCommand); cmd = msgCmd->cmd; } nng_msg_free(msg); if (cmd == DP_DC_Endoom) { log_info("Received Endoom from controller, quitting"); ctx->state = DP_DS_Endoom; if ((res = publish_state(ctx))) dp_nng_fatal("doom/publish_sate", res); I_Quit(); return; } } } { int res = 0; nng_msg *msg = NULL; if ((res = nng_msg_alloc(&msg, sizeof(MsgDoomFrame)))) dp_nng_fatal("doom/nng_msg_alloc", res); MsgDoomFrame *dpmsg = DP_NNG_BODY_AS(msg, MsgDoomFrame); dpmsg->head.msgType = DP_MT_DoomFrame; dpmsg->doomId = ctx->id; assert(g_frameBufferSize == DoomFrameSize); memcpy(dpmsg->frame, g_frameBuffer, g_frameBufferSize); if ((res = nng_sendmsg(ctx->pub, msg, 0))) dp_nng_fatal("ctrl/sendmsg", res); log_trace("doom(%d) published frame", ctx->id); } } void IB_GetColor(unsigned char *bytes, unsigned char red, unsigned char green, unsigned char blue) { #if DoomBytesPerPixel == 3 // FIXME: might be buggy bytes[0] = red; bytes[1] = green; bytes[2] = blue; #elif DoomBytesPerPixel == 4 bytes[0] = blue; bytes[1] = green; bytes[2] = red; bytes[3] = 0xff; // alpha? #else #error Unhandled DoomBytesPerPixel value. How to IB_GetColor? #endif } static void I_Quit_Wrapper(int dummy) { (void)dummy; log_info("Caught SIGINT, quitting"); I_Quit(); } void IB_InitGraphics(const char *title, size_t screen_width, size_t screen_height, size_t *bytes_per_pixel) { (void) title; log_set_level(LOG_DEBUG); g_frameBufferSize = screen_width * screen_height * DoomBytesPerPixel; assert(g_frameBufferSize == DoomFrameSize); g_frameBuffer = malloc(g_frameBufferSize); g_pitch = screen_width * DoomBytesPerPixel; *bytes_per_pixel = DoomBytesPerPixel; log_debug("IB_InitGraphics: w=%zu, h=%zu -> bytesPerPixel=%zu, frameBufferSize=%zu", screen_width, screen_height, *bytes_per_pixel, g_frameBufferSize); signal(SIGINT, I_Quit_Wrapper); dp_nng_init_limits(1, 1, 1); g_doomContext.pub = make_doom_pub(DoomUrlIpc); g_doomContext.sub = make_doom_sub(CtrlUrlIpc); g_doomContext.id = getpid(); g_doomContext.state = DP_DS_Ready; g_doomContext.f = do_doom_ready; DoomContext *ctx = &g_doomContext; log_info("waiting for go from doompanning"); while (true) { DP_DoomState prevState = ctx->state; ctx->f(ctx); if (prevState != ctx->state) { log_info("transition %s -> %s", doomstate_to_string(prevState), doomstate_to_string(ctx->state)); } if (ctx->state != DP_DS_Endoom && ctx->state != DP_DS_Running) usleep(10 * 1000); else break; } if (ctx->state == DP_DS_Endoom) { log_info("Received Endoom from controller, quitting"); I_Quit(); } } void IB_ShutdownGraphics(void) { nng_close(g_doomContext.pub); nng_close(g_doomContext.sub); free(g_frameBuffer); } void IB_GrabMouse(boolean grab) { (void) grab; }