/* ObjectWindows TApplication construction/destruction used by TDKPIN. */ #include "tdkpin_application.h" const char object_windows_error_format[] = "Error code = %d. Continue?"; const char object_windows_error_caption[] = "Application Error"; enum { APPLICATION_STATUS_OFFSET = 2, APPLICATION_NAME_OFFSET = 4, APPLICATION_FIELD_08_OFFSET = 8, APPLICATION_FIELD_0A_OFFSET = 10, APPLICATION_FIELD_0C_OFFSET = 12, APPLICATION_FIELD_0E_OFFSET = 14, APPLICATION_FIELD_10_OFFSET = 16, APPLICATION_INIT_APPLICATION_SLOT = 0x10, APPLICATION_INIT_INSTANCE_SLOT = 0x14, APPLICATION_PREPROCESS_MESSAGE_SLOT = 0x24, APPLICATION_DIALOG_MESSAGE_SLOT = 0x28, APPLICATION_ACCELERATOR_SLOT = 0x2c, APPLICATION_MDI_ACCELERATOR_SLOT = 0x30, APPLICATION_RUN_SLOT = 0x20, APPLICATION_ERROR_SLOT = 0x40, APPLICATION_SETUP_SLOT = 0x18, APPLICATION_CREATE_MAIN_WINDOW_SLOT = 0x34, APPLICATION_VALIDATE_WINDOW_SLOT = 0x3c, APPLICATION_ERROR_SLOT_FOR_WINDOW = 0x40, APPLICATION_IDLE_SLOT = 0x0c, APPLICATION_ACCELERATOR_OFFSET = 0x0c, APPLICATION_ACTIVE_DIALOG_OFFSET = 0x0e, APPLICATION_MAIN_WINDOW_OFFSET = 8, OBJECT_WINDOWS_BOUND_DISPATCH_OFFSET = 0x0133, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET = 4, OBJECT_WINDOWS_MDI_CLIENT_SLOT = 0x30, OBJECT_WINDOWS_CAN_CLOSE_SLOT = 0x3c, OBJECT_WINDOWS_CREATE_SLOT = 0x20, OBJECT_WINDOWS_EXEC_DIALOG_SLOT = 0x4c, WIN16_WM_QUIT = 0x0012, WIN16_PM_REMOVE = 1, WIN16_MB_YESNO_ICONHAND = 0x0014, WIN16_IDNO = 7, }; static void call_application_vmt_slot( Win16FarPtr application, uint16_t slot) { uint16_t vmt = win16_read_u16(application); Win16FarPtr procedure = win16_read_far_pointer(win16_dgroup_pointer(vmt), slot); win16_call_object_method(procedure, application); } /* * 1018:1a61 — construct ObjectWindows' application root. * * The process-global application pointer is published before virtual * initialization. InitApplication (+0x10) runs only for the first Win16 * instance; InitInstance (+0x14) runs only while the status word remains zero. */ Win16FarPtr object_windows_application_construct( Win16FarPtr object, uint16_t vmt, Win16FarPtr name) { BorlandObjectCallFrame frame = {object, vmt}; if (!borland_enter_object_constructor(&frame, 0)) { return frame.object; } frame.object = borland_default_object_constructor(frame.object, 0); win16_write_u16(frame.object, APPLICATION_NAME_OFFSET, win16_far_offset(name)); win16_write_u16(frame.object, APPLICATION_NAME_OFFSET + 2, win16_far_selector(name)); g_object_windows_application = frame.object; win16_write_u16(frame.object, APPLICATION_FIELD_0C_OFFSET, 0); win16_write_u16(frame.object, APPLICATION_STATUS_OFFSET, 0); win16_write_u16(frame.object, APPLICATION_FIELD_08_OFFSET, 0); win16_write_u16(frame.object, APPLICATION_FIELD_0A_OFFSET, 0); win16_write_u16(frame.object, APPLICATION_FIELD_0E_OFFSET, 0); win16_write_u16(frame.object, APPLICATION_FIELD_10_OFFSET, 0); g_object_windows_dispatch_proc = MakeProcInstance16( win16_relocated_code_pointer( 4, OBJECT_WINDOWS_BOUND_DISPATCH_OFFSET), g_win16_instance); borland_initialize_heap_reserve(); if (g_win16_previous_instance == 0) { call_application_vmt_slot( frame.object, APPLICATION_INIT_APPLICATION_SLOT); } if (win16_read_u16(win16_far_add_offset( frame.object, APPLICATION_STATUS_OFFSET)) == 0) { call_application_vmt_slot( frame.object, APPLICATION_INIT_INSTANCE_SLOT); } return frame.object; } /* 1018:1b08 — release the shared proc instance, then destroy/free the object. */ void object_windows_application_destruct(Win16FarPtr object, uint16_t vmt) { FreeProcInstance16(g_object_windows_dispatch_proc); borland_default_object_destructor(object, 0); BorlandObjectCallFrame frame = {object, vmt}; borland_finish_object_destructor(&frame, 0); } /* 1018:1b35 — default IdleAction: no background work was performed. */ bool object_windows_application_idle_default(Win16FarPtr application) { (void)application; return false; } /* 1018:1b4b — default first-instance InitApplication hook is intentionally empty. */ void object_windows_application_init_application_default( Win16FarPtr application) { (void)application; } /* * 1018:1bdf — publish the currently active dialog at object +0x0e/+0x10. * The modal-dialog path saves and restores this exact far pointer, and the * message loop uses it as the IsDialogMessage16 target. */ void object_windows_application_set_active_dialog( Win16FarPtr application, Win16FarPtr dialog) { win16_write_u16(application, APPLICATION_FIELD_0E_OFFSET, win16_far_offset(dialog)); win16_write_u16(application, APPLICATION_FIELD_10_OFFSET, win16_far_selector(dialog)); } static Win16FarPtr application_vmt_procedure( Win16FarPtr application, uint16_t slot) { uint16_t vmt = win16_read_u16(application); return win16_read_far_pointer(win16_dgroup_pointer(vmt), slot); } /* 1018:0e9e — show one ObjectWindows object only after it owns an HWND. */ void object_windows_show(Win16FarPtr object, INT16 command) { HWND16 window = win16_read_u16(win16_far_add_offset( object, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET)); if (window != 0) { (void)ShowWindow16(window, command); } } /* * 1018:1b57 — run setup, create/publish MainWindow, then show it. A null * creator result stores ObjectWindows status -5 instead. */ void object_windows_application_initialize(Win16FarPtr application) { win16_call_object_method( application_vmt_procedure(application, APPLICATION_SETUP_SLOT), application); Win16FarPtr main_window = win16_call_object_far_method( application_vmt_procedure( application, APPLICATION_CREATE_MAIN_WINDOW_SLOT), application); win16_write_u16(application, APPLICATION_MAIN_WINDOW_OFFSET, win16_far_offset(main_window)); win16_write_u16(application, APPLICATION_MAIN_WINDOW_OFFSET + 2, win16_far_selector(main_window)); if (main_window == 0) { win16_write_u16(application, APPLICATION_STATUS_OFFSET, 0xfffb); return; } object_windows_show(main_window, (INT16)g_win16_show_command); } /* 1018:1bb0 — enter the message loop, or report a preexisting status error. */ void object_windows_application_run(Win16FarPtr application) { uint16_t status = win16_read_u16(win16_far_add_offset( application, APPLICATION_STATUS_OFFSET)); if (status == 0) { win16_call_object_method( application_vmt_procedure(application, APPLICATION_RUN_SLOT), application); return; } win16_call_object_u16_method( application_vmt_procedure(application, APPLICATION_ERROR_SLOT), application, status); } /* * 1018:1bfc — ObjectWindows application message loop. * Only WM_QUIT terminates the loop. With an empty queue, a false IdleAction * result causes WaitMessage; either idle outcome then repeats the loop. */ void object_windows_application_message_loop(Win16FarPtr application) { MSG16 message; bool quit = false; do { if (PeekMessage16(&message, 0, 0, 0, WIN16_PM_REMOVE) != 0) { if (message.message == WIN16_WM_QUIT) { quit = true; } else { Win16FarPtr filter = application_vmt_procedure( application, APPLICATION_PREPROCESS_MESSAGE_SLOT); if (!win16_call_application_message_filter( filter, application, &message)) { (void)TranslateMessage16(&message); (void)DispatchMessage16(&message); } } } else { Win16FarPtr idle = application_vmt_procedure( application, APPLICATION_IDLE_SLOT); if (!win16_call_object_bool_method(idle, application)) { (void)WaitMessage16(); } } } while (!quit); win16_write_u16(application, APPLICATION_STATUS_OFFSET, message.wparam); } /* 1018:1c7f — dialog, MDI, then ordinary accelerator preprocessing. */ bool object_windows_application_preprocess_message( Win16FarPtr application, MSG16 *message) { static const uint16_t slots[] = { APPLICATION_DIALOG_MESSAGE_SLOT, APPLICATION_MDI_ACCELERATOR_SLOT, APPLICATION_ACCELERATOR_SLOT, }; for (size_t index = 0; index < sizeof(slots) / sizeof(slots[0]); index++) { Win16FarPtr filter = application_vmt_procedure(application, slots[index]); if (win16_call_application_message_filter( filter, application, message)) { return true; } } return false; } /* * 1018:1cd6 — route through the active dialog's HWND when present. * NEG AX; SBB AL,AL; NEG AL normalizes only AL, deliberately preserving AH * from the negated Win16 BOOL return. */ uint16_t object_windows_application_process_dialog_message( Win16FarPtr application, MSG16 *message) { Win16FarPtr dialog = win16_read_far_pointer(application, APPLICATION_ACTIVE_DIALOG_OFFSET); if (dialog == 0) { return 0; } HWND16 window = win16_read_u16(win16_far_add_offset( dialog, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET)); if (window == 0) { return 0; } uint16_t api_result = (uint16_t)IsDialogMessage16(window, message); uint16_t negated = (uint16_t)(0u - api_result); return (uint16_t)((negated & 0xff00u) | (api_result != 0 ? 1u : 0u)); } /* 1018:1d22 — process the application's ordinary accelerator table. */ bool object_windows_application_process_accelerator( Win16FarPtr application, MSG16 *message) { HACCEL16 accelerator = win16_read_u16(win16_far_add_offset( application, APPLICATION_ACCELERATOR_OFFSET)); if (accelerator == 0) { return false; } Win16FarPtr main_window = win16_read_far_pointer( application, APPLICATION_MAIN_WINDOW_OFFSET); HWND16 window = win16_read_u16(win16_far_add_offset( main_window, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET)); return TranslateAccelerator16(window, accelerator, message) != 0; } /* 1018:1d64 — ask the main window for its MDI client, then translate there. */ bool object_windows_application_process_mdi_accelerator( Win16FarPtr application, MSG16 *message) { Win16FarPtr main_window = win16_read_far_pointer( application, APPLICATION_MAIN_WINDOW_OFFSET); uint16_t main_vmt = win16_read_u16(main_window); Win16FarPtr getter = win16_read_far_pointer( win16_dgroup_pointer(main_vmt), OBJECT_WINDOWS_MDI_CLIENT_SLOT); Win16FarPtr client = win16_call_object_far_method(getter, main_window); if (client == 0) { return false; } HWND16 window = win16_read_u16(win16_far_add_offset( client, OBJECT_WINDOWS_OBJECT_HANDLE_OFFSET)); return TranslateMDISysAccel16(window, message) != 0; } /* 1018:1f56 — delegate application shutdown approval to MainWindow.CanClose. */ bool object_windows_application_can_close(Win16FarPtr application) { Win16FarPtr main_window = win16_read_far_pointer( application, APPLICATION_MAIN_WINDOW_OFFSET); uint16_t vmt = win16_read_u16(main_window); Win16FarPtr can_close = win16_read_far_pointer( win16_dgroup_pointer(vmt), OBJECT_WINDOWS_CAN_CLOSE_SLOT); return win16_call_object_bool_method(can_close, main_window); } /* 1018:10e9 — default TWindow VMT +0x30: this is not an MDI frame. */ Win16FarPtr object_windows_window_get_mdi_client_default( Win16FarPtr window) { (void)window; return 0; } static void report_application_error( Win16FarPtr application, uint16_t error) { win16_call_object_u16_method( application_vmt_procedure( application, APPLICATION_ERROR_SLOT_FOR_WINDOW), application, error); } /* * 1018:1db1 — validate a newly constructed window against heap-reserve and * object-status failures. Every failure reports through VMT +0x40 and invokes * the candidate's virtual destructor with dispose mode 1. OOM (-2) also * reprovisions the Borland emergency reserve afterward. */ Win16FarPtr object_windows_application_validate_window( Win16FarPtr application, Win16FarPtr window) { if (window == 0) { return 0; } if (borland_heap_reserve_is_unallocated()) { report_application_error(application, 0xfffe); borland_dispose_object(window); borland_ensure_heap_reserve(); return 0; } uint16_t status = win16_read_u16(win16_far_add_offset(window, 2)); if (status != 0) { report_application_error(application, status); borland_dispose_object(window); return 0; } return window; } /* 1018:1e39 — validate and create a modeless window, disposing failures. */ Win16FarPtr object_windows_application_make_window( Win16FarPtr application, Win16FarPtr window) { if (window == 0) { return 0; } Win16FarPtr validated = win16_call_application_window_method( application_vmt_procedure( application, APPLICATION_VALIDATE_WINDOW_SLOT), application, window); if (validated == 0) { return 0; } uint16_t vmt = win16_read_u16(window); Win16FarPtr create = win16_read_far_pointer( win16_dgroup_pointer(vmt), OBJECT_WINDOWS_CREATE_SLOT); if (win16_call_object_bool_method(create, window)) { return window; } report_application_error( application, win16_read_u16(win16_far_add_offset(window, 2))); borland_dispose_object(window); return 0; } /* * 1018:1eb0 — validate, execute, and always dispose a modal dialog. Negative * execution results are reported and translated to the fixed fallback 2. */ int16_t object_windows_application_exec_dialog( Win16FarPtr application, Win16FarPtr dialog) { int16_t result = 2; Win16FarPtr validated = win16_call_application_window_method( application_vmt_procedure( application, APPLICATION_VALIDATE_WINDOW_SLOT), application, dialog); if (validated == 0) { return result; } uint16_t vmt = win16_read_u16(dialog); Win16FarPtr execute = win16_read_far_pointer( win16_dgroup_pointer(vmt), OBJECT_WINDOWS_EXEC_DIALOG_SLOT); int16_t execute_result = win16_call_object_int_method(execute, dialog); if (execute_result < 0) { report_application_error(application, (uint16_t)execute_result); } else { result = execute_result; } borland_dispose_object(dialog); return result; } /* * 1018:1f14 — format and display one ObjectWindows application error. * The original passes a far pointer to the caller's uint16 error word as the * wvsprintf argument list. Choosing IDNO (7) exits with that full AX word. */ void object_windows_application_error( Win16FarPtr application, uint16_t error) { (void)application; char text[32]; uint16_t arguments[1] = {error}; (void)wvsprintf16(text, object_windows_error_format, arguments); INT16 response = win16_call_message_box( g_win16_message_box, 0, text, object_windows_error_caption, WIN16_MB_YESNO_ICONHAND); if (response == WIN16_IDNO) { borland_runtime_exit(error); } }