private void PerformSplit(int lines) { if (lines > 0) { if (upperWin.IsNull) { upperWin = Glk.glk_window_open(lowerWin, WinMethod.Above | WinMethod.Fixed, (uint)lines, WinType.TextGrid, 0); } else { Glk.glk_window_set_arrangement(Glk.glk_window_get_parent(upperWin), WinMethod.Above | WinMethod.Fixed, (uint)lines, winid_t.Null); } } else { if (!upperWin.IsNull) { stream_result_t dummy; Glk.glk_window_close(upperWin, out dummy); upperWin = winid_t.Null; currentWin = lowerWin; } } }
Stream IZMachineIO.OpenRestoreFile() { frefid_t file = Glk.glk_fileref_create_by_prompt( FileUsage.SavedGame | FileUsage.BinaryMode, FileMode.Read, 0); return(OpenStream(file, FileMode.Read)); }
Stream IZMachineIO.OpenAuxiliaryFile(string name, int size, bool writing) { frefid_t file = Glk.glk_fileref_create_by_name( FileUsage.Data | FileUsage.BinaryMode, name, 0); return(OpenStream(file, writing ? FileMode.Write : FileMode.Read)); }
private void RefreshTextStyle() { Style glkStyle = lastStyle; if (forceFixed) { switch (glkStyle) { case Style.Normal: glkStyle = Style.Preformatted; // fixed roman break; case Style.Subheader: glkStyle = Style.Header; // fixed bold break; case Style.Emphasized: glkStyle = Style.Note; // fixed italic break; case Style.Alert: glkStyle = Style.User1; // fixed bold+italic break; } } Glk.glk_set_style(glkStyle); }
Stream IZMachineIO.OpenSaveFile(int size) { frefid_t file = Glk.glk_fileref_create_by_prompt( FileUsage.SavedGame | FileUsage.BinaryMode, FileMode.Write, 0); return(OpenStream(file, FileMode.Write)); }
void IZMachineIO.PutTranscriptString(string str) { if (!transcriptStream.IsNull) { Glk.glk_put_string_stream(transcriptStream, str); } }
Stream IZMachineIO.OpenCommandFile(bool writing) { FileMode mode = writing ? FileMode.Write : FileMode.Read; frefid_t file = Glk.glk_fileref_create_by_prompt( FileUsage.InputRecord | FileUsage.TextMode, mode, 0); return(OpenStream(file, mode)); }
protected override void Dispose(bool disposing) { if (!gstr.IsNull) { stream_result_t dummy; Glk.glk_stream_close(gstr, out dummy); gstr = strid_t.Null; } }
void IZMachineIO.PutTranscriptChar(char ch) { if (ch > 255) { ch = '?'; } if (!transcriptStream.IsNull) { Glk.glk_put_char_stream(transcriptStream, (byte)ch); } }
void IZMachineIO.PutTextRectangle(string[] lines) { if (lineInputActive) { Glk.glk_cancel_line_event(currentWin, out canceledLineEvent); lineInputActive = false; } if (currentWin == lowerWin) { foreach (string str in lines) { if (unicode) { Glk.glk_put_string_uni(str); } else { Glk.glk_put_string(str); } Glk.glk_put_char((byte)'\n'); } } else { int oxpos = xpos; foreach (string str in lines) { Glk.glk_window_move_cursor(upperWin, (uint)oxpos, (uint)ypos); if (unicode) { Glk.glk_put_string_uni(str); } else { Glk.glk_put_string(str); } ypos++; if (ypos >= screenHeight) { ypos = (int)screenHeight - 1; } xpos = oxpos + str.Length; if (xpos >= screenWidth) { xpos = (int)screenWidth - 1; } } } }
public override void Write(byte[] buffer, int offset, int count) { if (offset == 0) { Glk.glk_put_buffer_stream(gstr, buffer, (uint)count); } else { byte[] temp = new byte[count]; Array.Copy(buffer, offset, temp, 0, count); Glk.glk_put_buffer_stream(gstr, temp, (uint)count); } }
void IZMachineIO.EraseLine() { if (currentWin == upperWin) { uint width, height; Glk.glk_window_get_size(upperWin, out width, out height); for (int i = xpos; i < width; i++) { Glk.glk_put_char((byte)' '); } Glk.glk_window_move_cursor(upperWin, (uint)xpos, (uint)ypos); } }
void IZMachineIO.PutChar(char ch) { if (lineInputActive) { Glk.glk_cancel_line_event(currentWin, out canceledLineEvent); lineInputActive = false; // glk_cancel_line_event prints a newline if (ch == '\n') { return; } } if (unicode) { Glk.glk_put_char_uni((uint)ch); } else { byte b; encodingChar[0] = ch; int result = Encoding.GetEncoding(Glk.LATIN1).GetBytes(encodingChar, 0, 1, encodedBytes, 0); if (result != 1) { b = (byte)'?'; } else { b = encodedBytes[0]; } Glk.glk_put_char(b); } if (currentWin == upperWin) { xpos++; uint width, height; Glk.glk_window_get_size(upperWin, out width, out height); if (xpos >= width) { xpos = 0; ypos++; if (ypos >= height) { ypos = (int)height - 1; } } } }
public override int Read(byte[] buffer, int offset, int count) { if (offset == 0) { return((int)Glk.glk_get_buffer_stream(gstr, buffer, (uint)count)); } else { byte[] temp = new byte[count]; int actual = (int)Glk.glk_get_buffer_stream(gstr, temp, (uint)count); Array.Copy(temp, 0, buffer, offset, actual); return(actual); } }
UnicodeCaps IZMachineIO.CheckUnicode(char ch) { UnicodeCaps result = 0; if (Glk.glk_gestalt(Gestalt.CharOutput, ch) != 0) { result |= UnicodeCaps.CanPrint; } if (Glk.glk_gestalt(Gestalt.CharInput, ch) != 0) { result |= UnicodeCaps.CanInput; } return(result); }
private void UpdateScreenSize() { if (!upperWin.IsNull) { uint width, height; Glk.glk_window_get_size(upperWin, out width, out height); screenWidth = width; if (SizeChanged != null) { SizeChanged(this, EventArgs.Empty); } } }
private Stream OpenStream(frefid_t fileref, FileMode mode) { if (fileref.IsNull) { return(null); } strid_t gstr = Glk.glk_stream_open_file(fileref, mode, 0); if (gstr.IsNull) { return(null); } return(new GlkStream(gstr)); }
public override long Seek(long offset, SeekOrigin origin) { SeekMode gseek; switch (origin) { case SeekOrigin.Begin: gseek = SeekMode.Start; break; case SeekOrigin.Current: gseek = SeekMode.Current; break; case SeekOrigin.End: gseek = SeekMode.End; break; default: throw new ArgumentOutOfRangeException("origin"); } Glk.glk_stream_set_position(gstr, (int)offset, gseek); return(Glk.glk_stream_get_position(gstr)); }
void IZMachineIO.SelectWindow(short num) { if (num == 0) { currentWin = lowerWin; } else if (num == 1) { /* work around a bug in some Inform games where the screen is erased, * destroying the split, but the split isn't restored before drawing * the status line. */ if (upperWin.IsNull) { ((IZMachineIO)this).SplitWindow(1); } currentWin = upperWin; } Glk.glk_set_window(currentWin); }
void IZMachineIO.MoveCursor(short x, short y) { // convert to Glk coordinates x--; y--; if (currentWin == upperWin) { uint width, height; Glk.glk_window_get_size(upperWin, out width, out height); if (x < 0) { xpos = 0; } else if (x >= width) { xpos = (int)width - 1; } else { xpos = x; } if (y < 0) { ypos = 0; } else if (y >= height) { ypos = (int)height - 1; } else { ypos = y; } Glk.glk_window_move_cursor(upperWin, (uint)xpos, (uint)ypos); } }
void IZMachineIO.EraseWindow(short num) { switch (num) { case 0: // lower only Glk.glk_window_clear(lowerWin); break; case 1: // upper only if (!upperWin.IsNull) { Glk.glk_window_clear(upperWin); } break; case -1: // erase both and unsplit if (!upperWin.IsNull) { stream_result_t dummy; Glk.glk_window_close(upperWin, out dummy); upperWin = winid_t.Null; } goto case -2; case -2: // erase both but keep split if (!upperWin.IsNull) { Glk.glk_window_clear(upperWin); } Glk.glk_window_clear(lowerWin); currentWin = lowerWin; xpos = 0; ypos = 0; break; } }
void IZMachineIO.SplitWindow(short lines) { uint curHeight; if (upperWin.IsNull) { curHeight = 0; } else { WinMethod method; winid_t keywin; Glk.glk_window_get_arrangement(Glk.glk_window_get_parent(upperWin), out method, out curHeight, out keywin); } targetSplit = lines; if (lines > curHeight) { PerformSplit(lines); } }
void IZMachineIO.PlaySoundSample(ushort number, SoundAction action, byte volume, byte repeats, SoundFinishedCallback callback) { switch (action) { case SoundAction.Prepare: Glk.glk_sound_load_hint(number, true); break; case SoundAction.FinishWith: Glk.glk_sound_load_hint(number, false); break; case SoundAction.Start: if (soundChannel.IsNull) { soundChannel = Glk.glk_schannel_create(0); } if (!soundChannel.IsNull) { volume = Math.Min(volume, (byte)8); Glk.glk_schannel_set_volume(soundChannel, (uint)(volume << 13)); soundCallback = callback; Glk.glk_schannel_play_ext(soundChannel, number, repeats, 1); } break; case SoundAction.Stop: if (!soundChannel.IsNull) { Glk.glk_schannel_stop(soundChannel); soundChannel = schanid_t.Null; soundCallback = null; } break; } }
void IZMachineIO.PutString(string str) { if (lineInputActive) { Glk.glk_cancel_line_event(currentWin, out canceledLineEvent); lineInputActive = false; // glk_cancel_line_event prints a newline if (str.Length > 0 && str[0] == '\n') { str = str.Substring(1); } } if (unicode) { Glk.glk_put_string_uni(str); } else { Glk.glk_put_string(str); } if (currentWin == upperWin) { xpos += str.Length; uint width, height; Glk.glk_window_get_size(upperWin, out width, out height); while (xpos >= width) { xpos -= (int)width; ypos++; if (ypos >= height) { ypos = (int)height - 1; } } } }
private void Dispose(bool disposing) { if (disposing) { Glk.glk_exit(); } if (argvStrings != null) { foreach (IntPtr str in argvStrings) { Marshal.FreeHGlobal(str); } argvStrings = null; } if (argv != IntPtr.Zero) { Marshal.FreeHGlobal(argv); argv = IntPtr.Zero; } GC.SuppressFinalize(this); }
string IZMachineIO.ReadLine(string initial, int time, TimedInputCallback callback, byte[] terminatingKeys, out byte terminator) { const int BUFSIZE = 256; IntPtr buf = Marshal.AllocHGlobal(unicode ? BUFSIZE * 4 : BUFSIZE); Encoding encoding = unicode ? Encoding.UTF32 : Encoding.GetEncoding(Glk.LATIN1); try { uint initlen = 0; if (initial.Length > 0) { if (unicode) { Glk.garglk_unput_string_uni(initial); } else { Glk.garglk_unput_string(initial); } byte[] initBytes = encoding.GetBytes(initial); Marshal.Copy(initBytes, 0, buf, initBytes.Length); initlen = (uint)initBytes.Length; if (unicode) { initlen /= 4; } } if (unicode) { Glk.glk_request_line_event_uni(currentWin, buf, BUFSIZE, initlen); } else { Glk.glk_request_line_event(currentWin, buf, BUFSIZE, initlen); } Glk.glk_request_timer_events((uint)(time * 100)); KeyCode[] glkTerminators = null; if (terminatingKeys != null && terminatingKeys.Length > 0) { glkTerminators = GlkKeysFromZSCII(terminatingKeys); Glk.garglk_set_line_terminators(currentWin, glkTerminators, (uint)glkTerminators.Length); } terminator = 0; event_t ev; bool done = false; do { Glk.glk_select(out ev); switch (ev.type) { case EvType.LineInput: if (ev.win == currentWin) { done = true; if (glkTerminators == null || ev.val2 == 0) { terminator = 13; } else { terminator = GlkKeyToZSCII((KeyCode)ev.val2); } } break; case EvType.Timer: lineInputActive = true; if (callback() == true) { done = true; } else if (!lineInputActive) { // the callback cancelled the line input request to print something... if (unicode) { Glk.glk_request_line_event_uni(currentWin, buf, BUFSIZE, canceledLineEvent.val1); } else { Glk.glk_request_line_event(currentWin, buf, BUFSIZE, canceledLineEvent.val1); } if (glkTerminators != null) { Glk.garglk_set_line_terminators(currentWin, glkTerminators, (uint)glkTerminators.Length); } } break; case EvType.Arrange: UpdateScreenSize(); break; case EvType.SoundNotify: SoundNotify(); break; } }while (!done); Glk.glk_request_timer_events(0); PerformSplit(targetSplit); // convert the string from Latin-1 or UTF-32 int length = (int)ev.val1; if (unicode) { length *= 4; } byte[] bytes = new byte[length]; Marshal.Copy(buf, bytes, 0, length); return(encoding.GetString(bytes)); } finally { Marshal.FreeHGlobal(buf); } }
void IZMachineIO.PutCommand(string command) { Glk.glk_set_style(Style.Input); ((IZMachineIO)this).PutString(command); RefreshTextStyle(); }
void IZMachineIO.SetTextStyle(TextStyle style) { Style glkStyle; switch (style) { case TextStyle.Roman: glkStyle = Style.Normal; Glk.garglk_set_reversevideo(false); break; case TextStyle.Reverse: Glk.garglk_set_reversevideo(true); return; case TextStyle.Bold: switch (lastStyle) { case Style.Normal: glkStyle = Style.Subheader; // prop bold break; case Style.Emphasized: glkStyle = Style.Alert; // prop bold+italic break; case Style.Preformatted: glkStyle = Style.Header; // fixed bold break; case Style.Note: glkStyle = Style.User1; // fixed bold+italic break; default: return; } break; case TextStyle.Italic: switch (lastStyle) { case Style.Normal: glkStyle = Style.Emphasized; // prop italic break; case Style.Subheader: glkStyle = Style.Alert; // prop bold+italic break; case Style.Preformatted: glkStyle = Style.Normal; // fixed italic break; case Style.Header: glkStyle = Style.User1; // fixed bold+italic break; default: return; } break; case TextStyle.FixedPitch: switch (lastStyle) { case Style.Normal: glkStyle = Style.Preformatted; // fixed roman break; case Style.Subheader: glkStyle = Style.Header; // fixed bold break; case Style.Emphasized: glkStyle = Style.Note; // fixed italic break; case Style.Alert: glkStyle = Style.User1; // fixed bold+italic break; default: return; } break; default: return; } lastStyle = glkStyle; RefreshTextStyle(); }
short IZMachineIO.ReadKey(int time, TimedInputCallback callback, CharTranslator translator) { PerformSplit(targetSplit); if (unicode) { Glk.glk_request_char_event_uni(currentWin); } else { Glk.glk_request_char_event(currentWin); } Glk.glk_request_timer_events((uint)(time * 100)); event_t ev; bool done = false; short result = 0; do { Glk.glk_select(out ev); switch (ev.type) { case EvType.CharInput: if (ev.win == currentWin) { if (ev.val1 <= 255 || (unicode && ev.val1 <= 0x10000)) { result = translator((char)ev.val1); } else { result = GlkKeyToZSCII((KeyCode)ev.val1); } if (result != 0) { done = true; } else if (unicode) { Glk.glk_request_char_event_uni(currentWin); } else { Glk.glk_request_char_event(currentWin); } } break; case EvType.Timer: if (callback() == true) { Glk.glk_cancel_char_event(currentWin); done = true; } break; case EvType.Arrange: UpdateScreenSize(); break; case EvType.SoundNotify: SoundNotify(); break; } }while (!done); Glk.glk_request_timer_events(0); return(result); }
public GlkIO(string[] args, string storyName) { // initialize Glk // first, add the application's path to the beginning of the arg list string[] newArgs = new string[args.Length + 1]; newArgs[0] = Application.ExecutablePath; Array.Copy(args, 0, newArgs, 1, args.Length); args = newArgs; // now, GarGlk keeps pointers into argv, so we have to copy the args into unmanaged memory argv = Marshal.AllocHGlobal(4 * (args.Length + 1)); argvStrings = new IntPtr[args.Length]; for (int i = 0; i < args.Length; i++) { IntPtr str = Marshal.StringToHGlobalAnsi(args[i]); argvStrings[i] = str; Marshal.WriteIntPtr(argv, 4 * i, str); } Marshal.WriteIntPtr(argv, 4 * args.Length, IntPtr.Zero); Glk.gli_startup(args.Length, argv); Glk.garglk_set_program_name("Demona"); Glk.garglk_set_program_info("Demona by Jesse McGrew\nA Glk interface for ZLR\nVersion " + ZMachine.ZLR_VERSION); Glk.garglk_set_story_name(storyName); // set style hints Glk.glk_stylehint_set(WinType.AllTypes, Style.User1, StyleHint.ReverseColor, 1); Glk.glk_stylehint_set(WinType.AllTypes, Style.User2, StyleHint.Weight, 1); Glk.glk_stylehint_set(WinType.AllTypes, Style.User2, StyleHint.Proportional, 0); // figure out how big the screen is winid_t tempWin = Glk.glk_window_open(winid_t.Null, 0, 0, WinType.TextGrid, 0); if (tempWin.IsNull) { screenWidth = 80; screenHeight = 25; } else { Glk.glk_window_get_size(tempWin, out screenWidth, out screenHeight); stream_result_t dummy; Glk.glk_window_close(tempWin, out dummy); } // open the lower window lowerWin = Glk.glk_window_open(winid_t.Null, 0, 0, WinType.TextBuffer, 0); if (lowerWin.IsNull) { throw new Exception("glk_window_open failed"); } Glk.glk_set_window(lowerWin); currentWin = lowerWin; xpos = 0; ypos = 0; unicode = (Glk.glk_gestalt(Gestalt.Unicode, 0) != 0); }