diff options
Diffstat (limited to 'src/uqm/cnctdlg.c')
-rw-r--r-- | src/uqm/cnctdlg.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/src/uqm/cnctdlg.c b/src/uqm/cnctdlg.c new file mode 100644 index 0000000..47824eb --- /dev/null +++ b/src/uqm/cnctdlg.c @@ -0,0 +1,630 @@ +//Copyright Michael Martin, 2006 + +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef NETPLAY + +#include "cnctdlg.h" +#include "controls.h" +#include "colors.h" +#include "gamestr.h" +#include "setup.h" +#include "units.h" +#include "resinst.h" +#include "nameref.h" +#include "libs/graphics/widgets.h" +#include "supermelee/netplay/netoptions.h" + +#define MCD_WIDTH 260 +#define MCD_HEIGHT 110 + +#define MENU_FRAME_RATE (ONE_SECOND / 20) + +typedef struct connect_dialog_state +{ + BOOLEAN (*InputFunc) (struct connect_dialog_state *pInputState); + + DWORD NextTime; + BOOLEAN Initialized; + int which_side; + + int confirmed; +} CONNECT_DIALOG_STATE; + +static void DrawConnectDialog (void); + +static WIDGET_MENU_SCREEN menu; +static WIDGET_BUTTON buttons[3]; +static WIDGET_SLIDER slider; +static WIDGET_TEXTENTRY texts[2]; + +static WIDGET *menu_widgets[] = { + (WIDGET *)&buttons[1], + (WIDGET *)&texts[0], + (WIDGET *)&buttons[0], + (WIDGET *)&slider, + (WIDGET *)&texts[1], + (WIDGET *)&buttons[2] }; + +static BOOLEAN done; + +/* This kind of sucks, but the Button callbacks need access to the + * CONNECT_DIALOG_STATE, so we need a pointer to it */ + +static CONNECT_DIALOG_STATE *current_state; + +static FONT PlayerFont; + +static int do_connect (WIDGET *self, int event); +static int do_listen (WIDGET *self, int event); +static int do_cancel (WIDGET *self, int event); + +static void +MCD_DrawMenuScreen (WIDGET *_self, int x, int y) +{ + int widget_index, widget_y; + + WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self; + + widget_y = y + 8; + for (widget_index = 0; widget_index < self->num_children; widget_index++) + { + WIDGET *c = self->child[widget_index]; + (*c->draw)(c, x, widget_y); + widget_y += (*c->height)(c) + 8; + } +} + +static void +MCD_DrawButton (WIDGET *_self, int x, int y) +{ + WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self; + Color oldtext; + Color inactive, selected; + FONT oldfont = SetContextFont (StarConFont); + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + + selected = MENU_HIGHLIGHT_COLOR; + inactive = MENU_TEXT_COLOR; + + t.baseline.x = 160; + t.baseline.y = y; + t.align = ALIGN_CENTER; + t.CharCount = ~0; + t.pStr = self->name; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (inactive); + } + font_DrawText (&t); + SetContextFontEffect (oldFontEffect); + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); + (void) x; +} + +static void +MCD_DrawSlider (WIDGET *_self, int x, int y) +{ + WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self; + Color oldtext; + Color default_color, selected; + FONT oldfont = SetContextFont (PlayerFont); + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + RECT r; + int tick = (MCD_WIDTH) / 8; + + default_color = MENU_TEXT_COLOR; + selected = MENU_HIGHLIGHT_COLOR; + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + r.corner.x = t.baseline.x + 3 * tick; + r.corner.y = t.baseline.y - 4; + r.extent.height = 2; + r.extent.width = 3 * tick; + DrawFilledRectangle (&r); + + r.extent.width = 3; + r.extent.height = 8; + r.corner.y = t.baseline.y - 7; + r.corner.x = t.baseline.x + 3 * tick + (3 * tick * + (self->value - self->min) / (self->max - self->min)) - 1; + DrawFilledRectangle (&r); + + (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y); + + SetContextFontEffect (oldFontEffect); + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +static void +MCD_DrawTextEntry (WIDGET *_self, int x, int y) +{ + WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self; + Color oldtext; + Color inactive, default_color, selected; + FONT oldfont = SetContextFont (PlayerFont); + FRAME oldFontEffect = SetContextFontEffect (NULL); + TEXT t; + + default_color = MENU_TEXT_COLOR; + selected = MENU_HIGHLIGHT_COLOR; + inactive = MENU_TEXT_COLOR; + + BatchGraphics (); + + t.baseline.x = x; + t.baseline.y = y; + t.align = ALIGN_LEFT; + t.CharCount = ~0; + t.pStr = self->category; + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (default_color); + } + font_DrawText (&t); + + /* Force string termination */ + self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0; + + t.baseline.y = y; + t.CharCount = utf8StringCount (self->value); + t.pStr = self->value; + + if (!(self->state & WTE_EDITING)) + { // normal or selected state + t.baseline.x = 160; + t.align = ALIGN_CENTER; + + if (widget_focus == _self) + { + oldtext = SetContextForeGroundColor (selected); + } + else + { + oldtext = SetContextForeGroundColor (inactive); + } + font_DrawText (&t); + } + else + { // editing state + COUNT i; + RECT text_r; + BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH]; + BYTE *pchar_deltas; + RECT r; + SIZE leading; + + t.baseline.x = x + 90; + t.align = ALIGN_LEFT; + + // calc background box dimensions + // XXX: this may need some tuning, especially if a + // different font is used. The font 'leading' values + // are not what they should be. +#define BOX_VERT_OFFSET 2 + GetContextFontLeading (&leading); + r.corner.x = t.baseline.x - 1; + r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET; + r.extent.width = MCD_WIDTH - r.corner.x - 10; + r.extent.height = leading + 2; + + TextRect (&t, &text_r, char_deltas); +#if 0 + // XXX: this should potentially be used in ChangeCallback + if ((text_r.extent.width + 2) >= r.extent.width) + { // the text does not fit the input box size and so + // will not fit when displayed later + UnbatchGraphics (); + // disallow the change + return (FALSE); + } +#endif + + oldtext = SetContextForeGroundColor (selected); + DrawFilledRectangle (&r); + + // calculate the cursor position and draw it + pchar_deltas = char_deltas; + for (i = self->cursor_pos; i > 0; --i) + r.corner.x += (SIZE)*pchar_deltas++; + if (self->cursor_pos < t.CharCount) /* cursor mid-line */ + --r.corner.x; + if (self->state & WTE_BLOCKCUR) + { // Use block cursor for keyboardless systems + if (self->cursor_pos == t.CharCount) + { // cursor at end-line -- use insertion point + r.extent.width = 1; + } + else if (self->cursor_pos + 1 == t.CharCount) + { // extra pixel for last char margin + r.extent.width = (SIZE)*pchar_deltas + 2; + } + else + { // normal mid-line char + r.extent.width = (SIZE)*pchar_deltas + 1; + } + } + else + { // Insertion point cursor + r.extent.width = 1; + } + // position cursor within input field rect + ++r.corner.x; + ++r.corner.y; + r.extent.height -= 2; + SetContextForeGroundColor (MENU_CURSOR_COLOR); + DrawFilledRectangle (&r); + + SetContextForeGroundColor (inactive); + font_DrawText (&t); + } + + UnbatchGraphics (); + SetContextFontEffect (oldFontEffect); + SetContextFont (oldfont); + SetContextForeGroundColor (oldtext); +} + +/* Text entry stuff, mostly C&Ped from setupmenu.c. Could use some + * refactoring, as redraw_menu () is the only real change. */ + +static BOOLEAN +OnTextEntryChange (TEXTENTRY_STATE *pTES) +{ + WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam; + + widget->cursor_pos = pTES->CursorPos; + if (pTES->JoystickMode) + widget->state |= WTE_BLOCKCUR; + else + widget->state &= ~WTE_BLOCKCUR; + + // XXX TODO: Here, we can examine the text entered so far + // to make sure it fits on the screen, for example, + // and return FALSE to disallow the last change + + return TRUE; // allow change +} + +static BOOLEAN +OnTextEntryFrame (TEXTENTRY_STATE *pTES) +{ + DrawConnectDialog (); + + SleepThreadUntil (pTES->NextTime); + pTES->NextTime = GetTimeCounter () + MENU_FRAME_RATE; + + (void) pTES; // satisfying compiler + return TRUE; // continue +} + +static int +OnTextEntryEvent (WIDGET_TEXTENTRY *widget) +{ // Going to edit the text + TEXTENTRY_STATE tes; + UNICODE revert_buf[256]; + + // position cursor at the end of text + widget->cursor_pos = utf8StringCount (widget->value); + widget->state = WTE_EDITING; + DrawConnectDialog (); + + // make a backup copy for revert on cancel + utf8StringCopy (revert_buf, sizeof (revert_buf), widget->value); + + // text entry setup + tes.Initialized = FALSE; + tes.NextTime = GetTimeCounter () + MENU_FRAME_RATE; + tes.BaseStr = widget->value; + tes.MaxSize = widget->maxlen; + tes.CursorPos = widget->cursor_pos; + tes.CbParam = widget; + tes.ChangeCallback = OnTextEntryChange; + tes.FrameCallback = OnTextEntryFrame; + + // SetMenuSounds (0, MENU_SOUND_SELECT); + if (!DoTextEntry (&tes)) + { // editing failed (canceled) -- revert the changes + utf8StringCopy (widget->value, widget->maxlen, revert_buf); + } + else + { + if (widget->onChange) + { + (*(widget->onChange))(widget); + } + } + + widget->state = WTE_NORMAL; + DrawConnectDialog (); + + return TRUE; // event handled +} + +/* Button response routines */ + +static int +do_connect (WIDGET *self, int event) +{ + if (event == WIDGET_EVENT_SELECT) + { + /* These assignments are safe exactly because texts[] is file-scope) */ + netplayOptions.peer[current_state->which_side].host = texts[0].value; + netplayOptions.peer[current_state->which_side].port = texts[1].value; + netplayOptions.peer[current_state->which_side].isServer = FALSE; + current_state->confirmed = TRUE; + netplayOptions.inputDelay = slider.value; + + done = TRUE; + } + (void)self; + return FALSE; +} + +static int +do_listen (WIDGET *self, int event) +{ + if (event == WIDGET_EVENT_SELECT) + { + /* These assignments are safe exactly because texts[] is file-scope) */ + netplayOptions.peer[current_state->which_side].port = texts[1].value; + netplayOptions.peer[current_state->which_side].isServer = TRUE; + netplayOptions.inputDelay = slider.value; + current_state->confirmed = TRUE; + done = TRUE; + } + (void)self; + return FALSE; +} + +static int +do_cancel (WIDGET *self, int event) +{ + if (event == WIDGET_EVENT_SELECT) + { + current_state->confirmed = FALSE; + done = TRUE; + } + (void)self; + return FALSE; +} + + +static void +CreateWidgets (void) +{ + int i; + + done = false; + + for (i = 0; i < 3; i++) + { + buttons[i].tag = WIDGET_TYPE_BUTTON; + buttons[i].parent = NULL; + buttons[i].receiveFocus = Widget_ReceiveFocusSimple; + buttons[i].draw = MCD_DrawButton; + buttons[i].height = Widget_HeightOneLine; + buttons[i].width = Widget_WidthFullScreen; + } + buttons[0].name = GAME_STRING (NETMELEE_STRING_BASE + 19); + // "Connect to remote host" + buttons[1].name = GAME_STRING (NETMELEE_STRING_BASE + 20); + // "Wait for incoming connection" + buttons[2].name = GAME_STRING (NETMELEE_STRING_BASE + 21); + // "Cancel" + + buttons[0].handleEvent = do_connect; + buttons[1].handleEvent = do_listen; + buttons[2].handleEvent = do_cancel; + + menu.tag = WIDGET_TYPE_MENU_SCREEN; + menu.parent = NULL; + menu.receiveFocus = Widget_ReceiveFocusMenuScreen; + menu.draw = MCD_DrawMenuScreen; + menu.height = Widget_HeightFullScreen; + menu.width = Widget_WidthFullScreen; + menu.num_children = 6; + menu.child = menu_widgets; + menu.handleEvent = Widget_HandleEventMenuScreen; + + slider.tag = WIDGET_TYPE_SLIDER; + slider.parent = NULL; + slider.handleEvent = Widget_HandleEventSlider; + slider.receiveFocus = Widget_ReceiveFocusSimple; + slider.draw = MCD_DrawSlider; + slider.height = Widget_HeightOneLine; + slider.width = Widget_WidthFullScreen; + slider.draw_value = Widget_Slider_DrawValue; + slider.min = 0; + slider.max = 9; + slider.step = 1; + slider.value = netplayOptions.inputDelay; + slider.category = GAME_STRING (NETMELEE_STRING_BASE + 24); + // "Net Delay" + + for (i = 0; i < 2; i++) + { + texts[i].tag = WIDGET_TYPE_TEXTENTRY; + texts[i].parent = NULL; + texts[i].handleEvent = Widget_HandleEventTextEntry; + texts[i].receiveFocus = Widget_ReceiveFocusSimple; + texts[i].draw = MCD_DrawTextEntry; + texts[i].height = Widget_HeightOneLine; + texts[i].width = Widget_WidthFullScreen; + texts[i].handleEventSelect = OnTextEntryEvent; + texts[i].maxlen = WIDGET_TEXTENTRY_WIDTH-1; + texts[i].state = WTE_NORMAL; + texts[i].cursor_pos = 0; + } + + texts[0].category = GAME_STRING (NETMELEE_STRING_BASE + 22); + // "Host" + texts[1].category = GAME_STRING (NETMELEE_STRING_BASE + 23); + // "Port" + + /* We sometimes assign to these internals; cannot strncpy over self! */ + if (texts[0].value != netplayOptions.peer[current_state->which_side].host) + { + strncpy (texts[0].value, + netplayOptions.peer[current_state->which_side].host, + texts[0].maxlen); + } + if (texts[1].value != netplayOptions.peer[current_state->which_side].port) + { + strncpy (texts[1].value, + netplayOptions.peer[current_state->which_side].port, + texts[1].maxlen); + } + texts[0].value[texts[0].maxlen]=0; + texts[1].value[texts[1].maxlen]=0; + + menu.receiveFocus ((WIDGET *)&menu, WIDGET_EVENT_DOWN); +} + +static void +DrawConnectDialog (void) +{ + RECT r; + + r.extent.width = MCD_WIDTH; + r.extent.height = MCD_HEIGHT; + r.corner.x = (SCREEN_WIDTH - r.extent.width) >> 1; + r.corner.y = (SCREEN_HEIGHT - r.extent.height) >> 1; + + + DrawShadowedBox (&r, SHADOWBOX_BACKGROUND_COLOR, + SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR); + + menu.draw ((WIDGET *)&menu, r.corner.x + 10, r.corner.y + 10); + +} + +static BOOLEAN +DoMeleeConnectDialog (CONNECT_DIALOG_STATE *state) +{ + BOOLEAN changed; + + /* Cancel any presses of the Pause key. */ + GamePaused = FALSE; + + if (!state->Initialized) + { + state->Initialized = TRUE; + SetDefaultMenuRepeatDelay (); + state->NextTime = GetTimeCounter (); + /* Prepare widgets, draw stuff, etc. */ + CreateWidgets (); + DrawConnectDialog (); + } + + changed = TRUE; + + if (PulsedInputState.menu[KEY_MENU_UP]) + { + Widget_Event (WIDGET_EVENT_UP); + } + else if (PulsedInputState.menu[KEY_MENU_DOWN]) + { + Widget_Event (WIDGET_EVENT_DOWN); + } + else if (PulsedInputState.menu[KEY_MENU_LEFT]) + { + Widget_Event (WIDGET_EVENT_LEFT); + } + else if (PulsedInputState.menu[KEY_MENU_RIGHT]) + { + Widget_Event (WIDGET_EVENT_RIGHT); + } + else if (PulsedInputState.menu[KEY_MENU_SELECT]) + { + Widget_Event (WIDGET_EVENT_SELECT); + } + else if (PulsedInputState.menu[KEY_MENU_CANCEL]) + { + Widget_Event (WIDGET_EVENT_CANCEL); + } + else if (PulsedInputState.menu[KEY_MENU_DELETE]) + { + Widget_Event (WIDGET_EVENT_DELETE); + } + else + { + changed = FALSE; + } + + if (changed) + { + DrawConnectDialog (); + } + + SleepThreadUntil (state->NextTime + MENU_FRAME_RATE); + state->NextTime = GetTimeCounter (); + return !((GLOBAL (CurrentActivity) & CHECK_ABORT) || + done); +} + +BOOLEAN +MeleeConnectDialog (int side) +{ + CONNECT_DIALOG_STATE state; + + PlayerFont = LoadFont (PLAYER_FONT); + + state.Initialized = FALSE; + state.which_side = side; + state.InputFunc = DoMeleeConnectDialog; + state.confirmed = TRUE; + + current_state = &state; + + DoInput (&state, TRUE); + + current_state = NULL; + + DestroyFont (PlayerFont); + + return state.confirmed; +} + +#endif /* NETPLAY */ + |