doompanning/src/dp_doom/linuxdoom-1.10/ib_video/ib_video_nng.c

376 lines
9 KiB
C
Raw Normal View History

// 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 <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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 <assert.h>
#include <nng/nng.h>
#include <nng/protocol/pubsub0/pub.h>
#include <nng/protocol/pubsub0/sub.h>
#include <dp_common.h>
#include <unistd.h>
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;
}