aboutsummaryrefslogtreecommitdiff
path: root/mousmain.c
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2022-04-09 14:55:06 +0200
committerJavier <dev.git@javispedro.com>2022-04-09 14:55:06 +0200
commit970f90228a6978712c28529437721caffec76202 (patch)
treee01f7997c7abceef1c8296ce77cc748947c12166 /mousmain.c
parent249c3ee375b3ded4f8abea747bf151ce20f40962 (diff)
downloadvbados-970f90228a6978712c28529437721caffec76202.tar.gz
vbados-970f90228a6978712c28529437721caffec76202.zip
rename source files for consistency
Diffstat (limited to 'mousmain.c')
-rw-r--r--mousmain.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/mousmain.c b/mousmain.c
new file mode 100644
index 0000000..5fe2246
--- /dev/null
+++ b/mousmain.c
@@ -0,0 +1,646 @@
+/*
+ * VBMouse - DOS mouse driver exec entry point
+ * Copyright (C) 2022 Javier S. Pedro
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <i86.h>
+
+#include "dlog.h"
+#include "int33.h"
+#include "int21dos.h"
+#include "ps2.h"
+#include "vbox.h"
+#include "vmware.h"
+#include "mousetsr.h"
+
+#if USE_WHEEL
+static void detect_wheel(LPTSRDATA data)
+{
+ // Do a quick check for a mouse wheel here.
+ // The TSR will do its own check when it is reset anyway
+ if (data->haswheel = ps2m_detect_wheel()) {
+ printf("Wheel mouse found and enabled\n");
+ }
+}
+
+static int set_wheel(LPTSRDATA data, bool enable)
+{
+ printf("Setting wheel support to %s\n", enable ? "enabled" : "disabled");
+ data->usewheel = enable;
+
+ if (data->usewheel) {
+ detect_wheel(data);
+ } else {
+ data->haswheel = false;
+ }
+
+ return 0;
+}
+
+static int set_wheel_key(LPTSRDATA data, const char *keyname)
+{
+ if (!data->usewheel || !data->haswheel) {
+ fprintf(stderr, "Wheel not detected or support not enabled\n");
+ return EXIT_FAILURE;
+ }
+ if (keyname) {
+ if (stricmp(keyname, "updn") == 0) {
+ data->wheel_up_key = 0x4800;
+ data->wheel_down_key = 0x5000;
+ printf("Generate Up Arrow / Down Arrow key presses on wheel movement\n");
+ } else if (stricmp(keyname, "pageupdn") == 0) {
+ data->wheel_up_key = 0x4900;
+ data->wheel_down_key = 0x5100;
+ printf("Generate PageUp / PageDown key presses on wheel movement\n");
+ } else {
+ fprintf(stderr, "Unknown key '%s'\n", keyname);
+ return EXIT_FAILURE;
+ }
+ } else {
+ printf("Disabling wheel keystroke generation\n");
+ data->wheel_up_key = 0;
+ data->wheel_down_key = 0;
+ }
+ return EXIT_SUCCESS;
+}
+#endif /* USE_WHEEL */
+
+#if USE_VIRTUALBOX
+static int set_virtualbox_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int err;
+
+ data->vbavail = false; // Reinitialize it even if already enabled
+
+ err = vbox_init_device(&data->vb);
+ if (err) {
+ fprintf(stderr, "Cannot find VirtualBox PCI device, err=%d\n", err);
+ return err;
+ }
+
+ printf("Found VirtualBox device at IO 0x%x\n", data->vb.iobase);
+
+ err = vbox_init_buffer(&data->vb);
+ if (err) {
+ fprintf(stderr, "Cannot lock buffer used for VirtualBox communication, err=%d\n", err);
+ return err;
+ }
+
+ err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS);
+ if (err) {
+ fprintf(stderr, "VirtualBox communication is not working, err=%d\n", err);
+ return err;
+ }
+
+ printf("VirtualBox integration enabled\n");
+ data->vbavail = true;
+ data->vbhaveabs = true;
+ } else {
+ if (data->vbavail) {
+ vbox_set_mouse(&data->vb, false, false);
+
+ vbox_release_buffer(&data->vb);
+
+ printf("Disabled VirtualBox integration\n");
+ data->vbavail = false;
+ data->vbhaveabs = false;
+ } else {
+ printf("VirtualBox integration already disabled or not available\n");
+ }
+ }
+
+ return 0;
+}
+
+static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable)
+{
+ printf("Setting host cursor to %s\n", enable ? "enabled" : "disabled");
+ data->vbwantcursor = enable;
+
+ return 0;
+}
+#endif
+
+#if USE_VMWARE
+static int set_vmware_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int32_t version;
+ uint32_t status;
+ uint16_t data_avail;
+
+ data->vmwavail = false;
+
+ version = vmware_get_version();
+ if (version < 0) {
+ fprintf(stderr, "Could not detect VMware, err=%ld\n", version);
+ return -1;
+ }
+
+ printf("Found VMware protocol version %ld\n", version);
+
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE);
+
+ status = vmware_abspointer_status();
+ if ((status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR)
+ == VMWARE_ABSPOINTER_STATUS_MASK_ERROR) {
+ fprintf(stderr, "VMware absolute pointer error, err=0x%lx\n",
+ status & VMWARE_ABSPOINTER_STATUS_MASK_ERROR);
+ return -1;
+ }
+
+ vmware_abspointer_data_clear();
+
+ // TSR part will enable the absolute mouse when reset
+
+ printf("VMware integration enabled\n");
+ data->vmwavail = true;
+ } else {
+ if (data->vmwavail) {
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_REQUEST_RELATIVE);
+ vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_DISABLE);
+
+ data->vmwavail = false;
+ printf("Disabled VMware integration\n");
+ } else {
+ printf("VMware integration already disabled or not available\n");
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int set_integration(LPTSRDATA data, bool enable)
+{
+ if (enable) {
+ int err = -1;
+
+#if USE_VIRTUALBOX
+ // First check if we can enable the VirtualBox integration,
+ // since it's a PCI device it's easier to check if it's not present
+ err = set_virtualbox_integration(data, true);
+ if (!err) return 0;
+#endif
+
+#if USE_VMWARE
+ // Afterwards try VMWare integration
+ err = set_vmware_integration(data, true);
+ if (!err) return 0;
+#endif
+
+ printf("Neither VirtualBox nor VMware integration available\n");
+ return err;
+ } else {
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ set_virtualbox_integration(data, false);
+ }
+#endif
+#if USE_VMWARE
+ if (data->vmwavail) {
+ set_vmware_integration(data, false);
+ }
+#endif
+ return 0;
+ }
+}
+
+static int set_host_cursor(LPTSRDATA data, bool enable)
+{
+#if USE_VIRTUALBOX
+ if (data->vbavail) {
+ return set_virtualbox_host_cursor(data, enable);
+ }
+#endif
+ printf("VirtualBox integration not available\n");
+ return -1;
+}
+
+static int configure_driver(LPTSRDATA data)
+{
+ int err;
+
+ // Configure the debug logging port
+ dlog_init();
+
+ // Check for PS/2 mouse BIOS availability
+ if ((err = ps2m_init(PS2M_PACKET_SIZE_PLAIN))) {
+ fprintf(stderr, "Cannot init PS/2 mouse BIOS, err=%d\n", err);
+ // Can't do anything without PS/2
+ return err;
+ }
+
+#if USE_WHEEL
+ // Let's utilize the wheel by default
+ data->usewheel = true;
+ data->wheel_up_key = 0;
+ data->wheel_down_key = 0;
+ detect_wheel(data);
+#endif
+
+#if USE_INTEGRATION
+ // Enable integration by default
+ set_integration(data, true);
+#endif
+
+#if USE_VIRTUALBOX
+ // Assume initially that we want host cursor
+ data->vbwantcursor = data->vbavail;
+#endif
+
+ return 0;
+}
+
+/** Converts bytes to MS-DOS "paragraphs" (16 bytes), rounding up. */
+static inline unsigned get_paragraphs(unsigned bytes)
+{
+ return (bytes + 15) / 16;
+}
+
+/** Gets the size of the resident part of this program, including the PSP. */
+static inline unsigned get_resident_program_size()
+{
+ return get_resident_size() + DOS_PSP_SIZE;
+}
+
+/** Deallocates the environment block from the passed PSP segment. */
+static void deallocate_environment(__segment psp)
+{
+ // TODO : Too lazy to make PSP struct;
+ // 0x2C is offsetof the environment block field on the PSP
+ uint16_t __far *envblockP = (uint16_t __far *) MK_FP(psp, 0x2C);
+ dos_free(*envblockP);
+ *envblockP = 0;
+}
+
+/** Copies a program to another location.
+ * @param new_seg PSP segment for the new location
+ * @param old_seg PSP segment for the old location
+ * @param size size of the program to copy including PSP size. */
+static void copy_program(__segment new_seg, __segment old_seg, unsigned size)
+{
+ // The MCB is always 1 segment before.
+ uint8_t __far *new_mcb = MK_FP(new_seg - 1, 0);
+ uint8_t __far *old_mcb = MK_FP(old_seg - 1, 0);
+ uint16_t __far *new_mcb_owner = (uint16_t __far *) &new_mcb[1];
+ char __far *new_mcb_owner_name = &new_mcb[8];
+ char __far *old_mcb_owner_name = &old_mcb[8];
+
+ // Copy entire resident segment including PSP
+ _fmemcpy(MK_FP(new_seg, 0), MK_FP(old_seg, 0), size);
+
+ // Make the new MCB point to itself as owner
+ *new_mcb_owner = new_seg;
+
+ // Copy the program name, too.
+ _fmemcpy(new_mcb_owner_name, old_mcb_owner_name, 8);
+}
+
+/** Allocates a UMB of the given size.
+ * If no UMBs are available, this may still return a block in conventional memory. */
+static __segment allocate_umb(unsigned size)
+{
+ bool old_umb_link = dos_query_umb_link_state();
+ unsigned int old_strategy = dos_query_allocation_strategy();
+ __segment new_segment;
+
+ dos_set_umb_link_state(true);
+ dos_set_allocation_strategy(DOS_FIT_BEST | DOS_FIT_HIGHONLY);
+
+ new_segment = dos_alloc(get_paragraphs(size));
+
+ dos_set_umb_link_state(old_umb_link);
+ dos_set_allocation_strategy(old_strategy);
+
+ return new_segment;
+}
+
+static int reallocate_to_umb(LPTSRDATA __far * data)
+{
+ const unsigned int resident_size = get_resident_program_size();
+ LPTSRDATA old_data = *data;
+ __segment old_psp_segment = FP_SEG(old_data) - (DOS_PSP_SIZE/16);
+ __segment new_psp_segment;
+
+ deallocate_environment(_psp);
+
+ // If we are already in UMA, don't bother
+ if (old_psp_segment >= 0xA000) {
+ return -1;
+ }
+
+ new_psp_segment = allocate_umb(resident_size);
+
+ if (new_psp_segment && new_psp_segment >= 0xA000) {
+ __segment new_segment = new_psp_segment + (DOS_PSP_SIZE/16);
+ printf("Moving to upper memory\n");
+
+ // Create a new program instance including PSP at the new_segment
+ copy_program(new_psp_segment, old_psp_segment, resident_size);
+
+ // Tell DOS to "switch" to the new program
+ dos_set_psp(new_psp_segment);
+
+ // Now update the data pointer to the new segment
+ *data = MK_FP(new_segment, FP_OFF(old_data));
+
+ return 0;
+ } else {
+ printf("No upper memory available\n");
+ if (new_psp_segment) {
+ // In case we got another low-memory segment...
+ dos_free(new_psp_segment);
+ }
+
+ return -1;
+ }
+}
+
+static __declspec(aborts) int install_driver(LPTSRDATA data, bool high)
+{
+ const unsigned int resident_size = DOS_PSP_SIZE + get_resident_size();
+
+ // No more interruptions from now on and until we TSR.
+ // Inserting ourselves in the interrupt chain should be atomic.
+ _disable();
+
+ data->prev_int33_handler = _dos_getvect(0x33);
+ _dos_setvect(0x33, data:>int33_isr);
+
+#if USE_WIN386
+ data->prev_int2f_handler = _dos_getvect(0x2f);
+ _dos_setvect(0x2f, data:>int2f_isr);
+#endif
+
+ printf("Driver installed\n");
+
+ // If we reallocated ourselves to UMB,
+ // it's time to free our initial conventional memory allocation
+ if (high) {
+ // We are about to free() our own code segment.
+ // Nothing should try to allocate memory between this and the TSR call
+ // below, since it could overwrite our code...
+ dos_free(_psp);
+ }
+
+ _dos_keep(EXIT_SUCCESS, get_paragraphs(resident_size));
+
+ // Shouldn't reach this part
+ return EXIT_FAILURE;
+}
+
+static bool check_if_driver_uninstallable(LPTSRDATA data)
+{
+ void (__interrupt __far *cur_int33_handler)() = _dos_getvect(0x33);
+
+ // Compare the segment of the installed handler to see if its ours
+ // or someone else's
+ if (FP_SEG(cur_int33_handler) != FP_SEG(data)) {
+ fprintf(stderr, "INT33 has been hooked by someone else, cannot safely remove\n");
+ return false;
+ }
+
+#if USE_WIN386
+ {
+ void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f);
+
+ if (FP_SEG(cur_int2f_handler) != FP_SEG(data)) {
+ fprintf(stderr, "INT2F has been hooked by someone else, cannot safely remove\n");
+ return false;
+ }
+ }
+#endif
+
+ return true;
+}
+
+static int unconfigure_driver(LPTSRDATA data)
+{
+#if USE_INTEGRATION
+ set_integration(data, false);
+#endif
+
+ ps2m_enable(false);
+ ps2m_set_callback(0);
+
+ return 0;
+}
+
+static int uninstall_driver(LPTSRDATA data)
+{
+ _dos_setvect(0x33, data->prev_int33_handler);
+
+#if USE_WIN386
+ _dos_setvect(0x2f, data->prev_int2f_handler);
+#endif
+
+ // Find and deallocate the PSP (including the entire program),
+ // it is always 256 bytes (16 paragraphs) before the TSR segment
+ dos_free(FP_SEG(data) - (DOS_PSP_SIZE/16));
+
+ printf("Driver uninstalled\n");
+
+ return 0;
+}
+
+static int driver_reset(void)
+{
+ printf("Reset mouse driver\n");
+ return int33_reset() == 0xFFFF;
+}
+
+static int driver_not_found(void)
+{
+ fprintf(stderr, "Driver data not found (driver not installed?)\n");
+ return EXIT_FAILURE;
+}
+
+static void print_help(void)
+{
+ printf("\n"
+ "Usage: \n"
+ " VBMOUSE <ACTION> <ARGS..>\n\n"
+ "Supported actions:\n"
+ " install install the driver (default)\n"
+ " low install in conventional memory (otherwise UMB)\n"
+ " uninstall uninstall the driver from memory\n"
+#if USE_WHEEL
+ " wheel <ON|OFF> enable/disable wheel API support\n"
+ " wheelkey <KEY|OFF> emulate a specific keystroke on wheel scroll\n"
+ " supported keys: updn, pageupdn\n"
+#endif
+#if USE_INTEGRATION
+ " integ <ON|OFF> enable/disable virtualbox integration\n"
+ " hostcur <ON|OFF> enable/disable mouse cursor rendering in host\n"
+#endif
+ " reset reset mouse driver settings\n"
+ );
+}
+
+static int invalid_arg(const char *s)
+{
+ fprintf(stderr, "Invalid argument '%s'\n", s);
+ print_help();
+ return EXIT_FAILURE;
+}
+
+static int arg_required(const char *s)
+{
+ fprintf(stderr, "Argument required for '%s'\n", s);
+ print_help();
+ return EXIT_FAILURE;
+}
+
+static bool is_true(const char *s)
+{
+ return stricmp(s, "yes") == 0
+ || stricmp(s, "y") == 0
+ || stricmp(s, "on") == 0
+ || stricmp(s, "true") == 0
+ || stricmp(s, "enabled") == 0
+ || stricmp(s, "enable") == 0
+ || stricmp(s, "1") == 0;
+}
+
+static bool is_false(const char *s)
+{
+ return stricmp(s, "no") == 0
+ || stricmp(s, "n") == 0
+ || stricmp(s, "off") == 0
+ || stricmp(s, "false") == 0
+ || stricmp(s, "disabled") == 0
+ || stricmp(s, "disable") == 0
+ || stricmp(s, "0") == 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ LPTSRDATA data = get_tsr_data(true);
+ int err, argi = 1;
+
+ printf("\nVBMouse %x.%x (like MSMOUSE %x.%x)\n", VERSION_MAJOR, VERSION_MINOR, REPORTED_VERSION_MAJOR, REPORTED_VERSION_MINOR);
+
+ if (argi >= argc || stricmp(argv[argi], "install") == 0) {
+ bool high = true;
+
+ argi++;
+ for (; argi < argc; argi++) {
+ if (stricmp(argv[argi], "low") == 0) {
+ high = false;
+ } else if (stricmp(argv[argi], "high") == 0) {
+ high = true;
+ } else {
+ return invalid_arg(argv[argi]);
+ }
+ }
+
+ if (data) {
+ printf("VBMouse already installed\n");
+ print_help();
+ return EXIT_SUCCESS;
+ }
+
+ data = get_tsr_data(false);
+ if (high) {
+ err = reallocate_to_umb(&data);
+ if (err) high = false; // Not fatal
+ } else {
+ deallocate_environment(_psp);
+ }
+ err = configure_driver(data);
+ if (err) return EXIT_FAILURE;
+ return install_driver(data, high);
+ } else if (stricmp(argv[argi], "uninstall") == 0) {
+ if (!data) return driver_not_found();
+ if (!check_if_driver_uninstallable(data)) {
+ return EXIT_FAILURE;
+ }
+ err = unconfigure_driver(data);
+ if (err) {
+ return EXIT_FAILURE;
+ }
+ return uninstall_driver(data);
+#if USE_WHEEL
+ } else if (stricmp(argv[argi], "wheel") == 0) {
+ bool enable = true;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ }
+
+ return set_wheel(data, enable);
+ } else if (stricmp(argv[argi], "wheelkey") == 0) {
+ bool enable = true;
+ const char *key = 0;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ else key = argv[argi];
+ }
+
+ if (enable) {
+ if (!key) return arg_required("wheelkey");
+ return set_wheel_key(data, key);
+ } else {
+ return set_wheel_key(data, 0);
+ }
+#endif
+#if USE_INTEGRATION
+ } else if (stricmp(argv[argi], "integ") == 0) {
+ bool enable = true;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ }
+
+ return set_integration(data, enable);
+ } else if (stricmp(argv[argi], "hostcur") == 0) {
+ bool enable = true;
+
+ if (!data) return driver_not_found();
+
+ argi++;
+ if (argi < argc) {
+ if (is_false(argv[argi])) enable = false;
+ }
+
+ // Reset before changing this to ensure the cursor is not drawn
+ int33_reset();
+
+ return set_host_cursor(data, enable);
+#endif
+ } else if (stricmp(argv[argi], "reset") == 0) {
+ return driver_reset();
+ } else {
+ return invalid_arg(argv[argi]);
+ }
+}