/* A demo GTK application in Pawn, using gtk-server and the "process control" * module for Pawn. This example also illustrates the use of (communicating) * finite state machines. */ #include #include enum Btn { Btn0, Btn1, Btn2, Btn3, Btn4, Btn5, Btn6, Btn7, Btn8, Btn9, BtnDot, BtnC, BtnCE, BtnPlus, BtnMin, BtnMul, BtnDiv, BtnEqual, BtnNone, } static entry /* GTK "entry" widget */ static numberstring[40 char] static accum static Btn:pending_op adddigit(digit, bool:reset = false) { new command[80 char], charstr[2 char] charstr{0} = (0 <= digit <= 9) ? digit + '0' : digit if (reset) numberstring[0] = EOS strcat numberstring, charstr strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring GTK(command) } displayresult(value) { new command[80 char] valstr numberstring, value, true strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring GTK(command) } resetentry(digit) { adddigit '-', .reset = true adddigit digit } resetentry(digit) <> { adddigit digit, .reset = true } event_0() { resetentry 0 } event_0() { adddigit 0 } event_1_9(Btn:idx) { resetentry _:(idx - Btn0) state number:int } event_1_9(Btn:idx) { adddigit _:(idx - Btn0) } event_dot() { resetentry 0 adddigit '.' state number:frac } event_dot() { adddigit '.' state number:frac } event_dot() { /* reject entry */ } event_minus() { state number:negated resetentry 0 } event_minus() <> { event_oper BtnMin /* forward to the "calc" automaton */ } /* helper function for the calculator automaton */ performoperation() { /* get the other operand, perform the operation */ new val = strval(numberstring) switch (pending_op) { case BtnPlus: accum += val case BtnMin: accum -= val case BtnMul: accum *= val case BtnDiv: accum = (val == 0) ? 0 : accum / val } displayresult accum } event_oper(Btn:idx) { /* save operand and operator */ accum = strval(numberstring) pending_op = idx state number:zero state calc:pending } event_oper(Btn:idx) { performoperation /* do the pending operation */ pending_op = idx /* save the operator for the next operation */ state number:zero } event_equal() { /* ignore */ } event_equal() { performoperation /* do the pending operation */ state calc:idle /* reset the calculator */ state number:zero } event_C() { state calc:idle state number:zero resetentry 0 } event_CE() { state number:zero resetentry 0 } main() { if (!procexec("gtk-server -stdin") && !procexec("gtk-server.exe -stdin")) fatal "unable to launch gtk-server" /* make a window */ GTK("gtk_init NULL NULL") new win = GTK("gtk_window_new 0") GTK("gtk_window_set_title %d \"Pawn calculator\"", win) GTK("gtk_widget_set_usize %d 200 200", win) /* add a table (align the other controls) */ new table = GTK("gtk_table_new 50 50 1") GTK("gtk_container_add %d %d", win, table) /* the number entry */ entry = GTK("gtk_entry_new") GTK("gtk_table_attach_defaults %d %d 1 49 1 9", table, entry) /* the key pad */ new buttons[Btn] buttons[BtnDot] = GTK("gtk_button_new_with_label .") GTK("gtk_table_attach_defaults %d %d 21 29 41 49", table, buttons[BtnDot]) buttons[Btn0] = GTK("gtk_button_new_with_label 0") GTK("gtk_table_attach_defaults %d %d 1 19 41 49", table, buttons[Btn0]) buttons[Btn1] = GTK("gtk_button_new_with_label 1") GTK("gtk_table_attach_defaults %d %d 1 9 31 39", table, buttons[Btn1]) buttons[Btn2] = GTK("gtk_button_new_with_label 2") GTK("gtk_table_attach_defaults %d %d 11 19 31 39", table, buttons[Btn2]) buttons[Btn3] = GTK("gtk_button_new_with_label 3") GTK("gtk_table_attach_defaults %d %d 21 29 31 39", table, buttons[Btn3]) buttons[Btn4] = GTK("gtk_button_new_with_label 4") GTK("gtk_table_attach_defaults %d %d 1 9 21 29", table, buttons[Btn4]) buttons[Btn5] = GTK("gtk_button_new_with_label 5") GTK("gtk_table_attach_defaults %d %d 11 19 21 29", table, buttons[Btn5]) buttons[Btn6] = GTK("gtk_button_new_with_label 6") GTK("gtk_table_attach_defaults %d %d 21 29 21 29", table, buttons[Btn6]) buttons[Btn7] = GTK("gtk_button_new_with_label 7") GTK("gtk_table_attach_defaults %d %d 1 9 11 19", table, buttons[Btn7]) buttons[Btn8] = GTK("gtk_button_new_with_label 8") GTK("gtk_table_attach_defaults %d %d 11 19 11 19", table, buttons[Btn8]) buttons[Btn9] = GTK("gtk_button_new_with_label 9") GTK("gtk_table_attach_defaults %d %d 21 29 11 19", table, buttons[Btn9]) buttons[BtnC] = GTK("gtk_button_new_with_label C") GTK("gtk_table_attach_defaults %d %d 31 39 11 19", table, buttons[BtnC]) buttons[BtnCE] = GTK("gtk_button_new_with_label CE") GTK("gtk_table_attach_defaults %d %d 41 49 11 19", table, buttons[BtnCE]) buttons[BtnPlus] = GTK("gtk_button_new_with_label +") GTK("gtk_table_attach_defaults %d %d 31 39 21 29", table, buttons[BtnPlus]) buttons[BtnMin] = GTK("gtk_button_new_with_label -") GTK("gtk_table_attach_defaults %d %d 41 49 21 29", table, buttons[BtnMin]) buttons[BtnMul] = GTK("gtk_button_new_with_label x") GTK("gtk_table_attach_defaults %d %d 31 39 31 39", table, buttons[BtnMul]) buttons[BtnDiv] = GTK("gtk_button_new_with_label /") GTK("gtk_table_attach_defaults %d %d 41 49 31 39", table, buttons[BtnDiv]) buttons[BtnEqual] = GTK("gtk_button_new_with_label =") GTK("gtk_table_attach_defaults %d %d 31 49 41 49", table, buttons[BtnEqual]) /* initialize automata */ state number:zero state calc:idle /* wait for events, and dispatch them */ GTK("gtk_widget_show_all %d", win) resetentry 0 new event new Btn:idx do { event = GTK("gtk_server_callback wait") /* find the button matching the event, generate the event */ for (idx = Btn0; idx < BtnNone && buttons[idx] != event; idx++) {} switch (idx) { case Btn0: event_0 case Btn1 .. Btn9: event_1_9 idx case BtnDot: event_dot case BtnMin: event_minus case BtnPlus, BtnMul, BtnDiv: event_oper idx case BtnEqual: event_equal case BtnC: event_C case BtnCE: event_CE } } while (event != win); /* direct call, because we must not wait for a reply on this command */ procwrite "gtk_server_exit", true } GTK(const format[], ...) { new command[256 char] switch (numargs()) { case 1: strpack command, format case 2: strformat command, _, true, format, getarg(1) case 3: strformat command, _, true, format, getarg(1), getarg(2) case 4: strformat command, _, true, format, getarg(1), getarg(2), getarg(3) case 5: strformat command, _, true, format, getarg(1), getarg(2), getarg(3), getarg(4) } procwrite command, true new reply[30] procread reply, .striplf=true if (strcmp(reply, "ok") == 0) return true return strval(reply) } fatal(const message[]) { printf "FATAL: %s\n", message exit }