/* * * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. * Copyright 1993 by David Dawes <dawes@xfree86.org> * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich * Copyright 1994-2002 by The XFree86 Project, Inc. * Copyright 2002 by Paul Elliott * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the names of copyright holders not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. The copyright holders * make no representations about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * 3 button emulation stuff * based on the emulation method in xf86-input-mouse/dist/src/mouse.c */ #include "inpututils.h" #include "mouseEmu3btn.h" static CARD32 buttonTimer(MouseEmu3btnPtr pEmu3btn); static void Emulate3ButtonsSetEnabled(MouseEmu3btnPtr pEmu3btn, Bool enable); static Bool Emulate3ButtonsSoft(MouseEmu3btnPtr pEmu3btn); static void MouseBlockHandler(void *data, void *waitTime); static void MouseWakeupHandler(void *data, int i); /********************************************************************** * * Emulate3Button support code * **********************************************************************/ /* * Lets create a simple finite-state machine for 3 button emulation: * * We track buttons 1 and 3 (left and right). There are 11 states: * 0 ground - initial state * 1 delayed left - left pressed, waiting for right * 2 delayed right - right pressed, waiting for left * 3 pressed middle - right and left pressed, emulated middle sent * 4 pressed left - left pressed and sent * 5 pressed right - right pressed and sent * 6 released left - left released after emulated middle * 7 released right - right released after emulated middle * 8 repressed left - left pressed after released left * 9 repressed right - right pressed after released right * 10 pressed both - both pressed, not emulating middle */ #define ST_INVALID -1 #define ST_GROUND 0 /* initial state */ #define ST_DELAYED_LEFT 1 /* left pressed and waiting timeout */ #define ST_DELAYED_RIGHT 2 /* right pressed and waiting timeout */ #define ST_PRESSED_MIDDLE 3 /* middle pressed deteremined */ #define ST_PRESSED_LEFT 4 /* left pressed determined */ #define ST_PRESSED_RIGHT 5 /* right pressed determined */ #define ST_RELEASED_LEFT 6 /* left released after pressed both */ #define ST_RELEASED_RIGHT 7 /* right released after pressed both */ #define ST_REPRESSED_LEFT 8 /* left repressed after release */ #define ST_REPRESSED_RIGHT 9 /* right repressed after release */ #define ST_PRESSED_BOTH 10 /* both pressed (not as middle) */ #define NSTATES 11 /* * At each state, we need handlers for the following events * 0: no buttons down * 1: left button down * 2: right button down * 3: both buttons down * 4: emulate3Timeout passed without a button change * Note that button events are not deltas, they are the set of buttons being * pressed now. It's possible (ie, mouse hardware does it) to go from (eg) * left down to right down without anything in between, so all cases must be * handled. * * a handler consists of three values: * 0: action1 * 1: action2 * 2: new emulation state */ struct button_event { int type; /* ButtonNone / ButtonPress / ButtonRelease */ #define ButtonNone 0 int button; #define ButtonLeft Button1 #define ButtonMiddle Button2 #define ButtonRight Button3 }; struct button_action { struct button_event event1; struct button_event event2; int new_state; }; /* The set of buttons being pressed passed from DDX mouse events */ #define BMASK_LEFT 0x01 #define BMASK_MIDDLE 0x02 #define BMASK_RIGHT 0x04 /* Event index values per buttons being pressed */ #define EMU_BUTTONS_NONE 0 #define EMU_BUTTONS_LEFT 1 #define EMU_BUTTONS_RIGHT 2 #define EMU_BUTTONS_BOTH 3 #define NEMU_BUTTONSTATE 4 #define BMASKTOINDEX(bmask) \ ((((bmask) & BMASK_RIGHT) >> 1) | ((bmask) & BMASK_LEFT)) struct button_state { struct button_action buttons[NEMU_BUTTONSTATE]; struct button_action timeout; }; /* * The comment preceeding each section is the current emulation state. * The comments to the right are of the form * <button state> (<events>) -> <new emulation state> * which should be read as * If the buttons are in <button state>, generate <events> then go to * <new emulation state>. */ static const struct button_state stateTab[NSTATES] = { /* 0 ground - initial state */ [ST_GROUND] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing -> ground (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left -> delayed left */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right -> delayed right */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (middle press) -> pressed middle */ .event1 = { ButtonPress, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_MIDDLE, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 1 delayed left - left pressed, waiting for right */ [ST_DELAYED_LEFT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (left event) -> ground */ .event1 = { ButtonPress, ButtonLeft }, .event2 = { ButtonRelease, ButtonLeft }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left -> delayed left (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (left event) -> delayed right */ .event1 = { ButtonPress, ButtonLeft }, .event2 = { ButtonRelease, ButtonLeft }, .new_state = ST_DELAYED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (middle press) -> pressed middle */ .event1 = { ButtonPress, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_MIDDLE, }, .timeout = { /* timeout (left press) -> pressed left */ .event1 = { ButtonPress, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_LEFT, }, }, /* 2 delayed right - right pressed, waiting for left */ [ST_DELAYED_RIGHT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (right event) -> ground */ .event1 = { ButtonPress, ButtonRight }, .event2 = { ButtonRelease, ButtonRight }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (right event) -> delayed left */ .event1 = { ButtonPress, ButtonRight }, .event2 = { ButtonRelease, ButtonRight }, .new_state = ST_DELAYED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right -> delayed right (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (middle press) -> pressed middle */ .event1 = { ButtonPress, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_MIDDLE, }, .timeout = { /* timeout (right press) -> pressed right */ .event1 = { ButtonPress, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_RIGHT, }, }, /* 3 pressed middle - right and left pressed, emulated middle sent */ [ST_PRESSED_MIDDLE] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (middle release) -> ground */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left -> released right */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_RIGHT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right -> released left */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_LEFT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right -> pressed middle (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_MIDDLE, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 4 pressed left - left pressed and sent */ [ST_PRESSED_LEFT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (left release) -> ground */ .event1 = { ButtonRelease, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left -> pressed left (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (left release) -> delayed right */ .event1 = { ButtonRelease, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (right press) -> pressed both */ .event1 = { ButtonPress, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_BOTH, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 5 pressed right - right pressed and sent */ [ST_PRESSED_RIGHT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (right release) -> ground */ .event1 = { ButtonRelease, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (right release) -> delayed left */ .event1 = { ButtonRelease, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right -> pressed right (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (left press) -> pressed both */ .event1 = { ButtonPress, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_BOTH, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 6 released left - left released after emulated middle */ [ST_RELEASED_LEFT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (middle release) -> ground */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (middle release) -> delayed left */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right -> released left (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_LEFT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (left press) -> repressed left */ .event1 = { ButtonPress, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_REPRESSED_LEFT, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 7 released right - right released after emulated middle */ [ST_RELEASED_RIGHT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (middle release) -> ground */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left -> released right (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_RIGHT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (middle release) -> delayed right */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_DELAYED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right (right press) -> repressed right */ .event1 = { ButtonPress, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_REPRESSED_RIGHT, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 8 repressed left - left pressed after released left */ [ST_REPRESSED_LEFT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (middle release, left release) -> ground */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonRelease, ButtonLeft }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (middle release) -> pressed left */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (left release) -> released left */ .event1 = { ButtonRelease, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_LEFT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right -> repressed left (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_REPRESSED_LEFT, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 9 repressed right - right pressed after released right */ [ST_REPRESSED_RIGHT] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (middle release, right release) -> ground */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonRelease, ButtonRight }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (right release) -> released right */ .event1 = { ButtonRelease, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_RELEASED_RIGHT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (middle release) -> pressed right */ .event1 = { ButtonRelease, ButtonMiddle }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right -> repressed right (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_REPRESSED_RIGHT, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, /* 10 pressed both - both pressed, not emulating middle */ [ST_PRESSED_BOTH] = { .buttons[EMU_BUTTONS_NONE] = { /* nothing (left release, right release) -> ground */ .event1 = { ButtonRelease, ButtonLeft }, .event2 = { ButtonRelease, ButtonRight }, .new_state = ST_GROUND, }, .buttons[EMU_BUTTONS_LEFT] = { /* left (right release) -> pressed left */ .event1 = { ButtonRelease, ButtonRight }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_LEFT, }, .buttons[EMU_BUTTONS_RIGHT] = { /* right (left release) -> pressed right */ .event1 = { ButtonRelease, ButtonLeft }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_RIGHT, }, .buttons[EMU_BUTTONS_BOTH] = { /* left & right -> pressed both (no change) */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_PRESSED_BOTH, }, .timeout = { /* timeout N/A */ .event1 = { ButtonNone, 0 }, .event2 = { ButtonNone, 0 }, .new_state = ST_INVALID, }, }, }; static CARD32 buttonTimer(MouseEmu3btnPtr pEmu3btn) { sigset_t sigmask; int type, button, flag; ValuatorMask mask; const struct button_action *timeout_action; (void)sigemptyset(&sigmask); (void)sigaddset(&sigmask, SIGIO); (void)sigprocmask(SIG_BLOCK, &sigmask, NULL); pEmu3btn->emulate3Pending = FALSE; timeout_action = &stateTab[pEmu3btn->emulateState].timeout; if ((type = timeout_action->event1.type) != ButtonNone) { button = timeout_action->event1.button; flag = POINTER_RELATIVE; valuator_mask_zero(&mask); QueuePointerEvents(pEmu3btn->device, type, button, flag, &mask); pEmu3btn->emulateState = timeout_action->new_state; } else { LogMessageVerbSigSafe(X_WARNING, -1, "Got unexpected buttonTimer in state %d\n", pEmu3btn->emulateState); } (void)sigprocmask(SIG_UNBLOCK, &sigmask, NULL); return 0; } static void Emulate3ButtonsSetEnabled(MouseEmu3btnPtr pEmu3btn, Bool enable) { if (pEmu3btn->emulate3Buttons == enable) return; pEmu3btn->emulate3Buttons = enable; if (enable) { pEmu3btn->emulateState = ST_GROUND; pEmu3btn->emulate3Pending = FALSE; pEmu3btn->emulate3ButtonsSoft = FALSE; /* specifically requested now */ RegisterBlockAndWakeupHandlers(MouseBlockHandler, MouseWakeupHandler, (void *)pEmu3btn); } else { if (pEmu3btn->emulate3Pending) buttonTimer(pEmu3btn); RemoveBlockAndWakeupHandlers(MouseBlockHandler, MouseWakeupHandler, (void *)pEmu3btn); } } static Bool Emulate3ButtonsSoft(MouseEmu3btnPtr pEmu3btn) { if (!pEmu3btn->emulate3ButtonsSoft) return TRUE; #if defined(__NetBSD__) && defined(WSCONS_SUPPORT) /* * On NetBSD a wsmouse is a multiplexed device. Imagine a notebook * with two-button mousepad, and an external USB mouse plugged in * temporarily. After using button 3 on the external mouse and * unplugging it again, the mousepad will still need to emulate * 3 buttons. */ return TRUE; #else LogMessageVerbSigSafe(X_INFO, 4, "mouse: 3rd Button detected: disabling emulate3Button\n"); Emulate3ButtonsSetEnabled(pEmu3btn, FALSE); return FALSE; #endif } static void MouseBlockHandler(void *data, void *waitTime) { MouseEmu3btnPtr pEmu3btn = data; int ms; if (pEmu3btn->emulate3Pending) { ms = pEmu3btn->emulate3Expires - GetTimeInMillis(); if (ms <= 0) ms = 0; AdjustWaitForDelay(waitTime, ms); } } static void MouseWakeupHandler(void *data, int i) { MouseEmu3btnPtr pEmu3btn = data; int ms; if (pEmu3btn->emulate3Pending) { ms = pEmu3btn->emulate3Expires - GetTimeInMillis(); if (ms <= 0) buttonTimer(pEmu3btn); } } /******************************************************************* * function "Emulate3ButtonsEnable" * * purpose: * Enable and initialize Emulate3Buttons structures. * argument: * (MouseEmu3btnPtr)pEmu3btn : Emu3btn private record * (DeviceIntPtr)device : pointer device private record * (int)timeout : timeout to wait another button [ms] * *******************************************************************/ void Emulate3ButtonsEnable(MouseEmu3btnPtr pEmu3btn, DeviceIntPtr device, int timeout) { BUG_RETURN_MSG(device == NULL, "Invalid DeviceIntPtr.\n"); if (timeout <= 0) { timeout = EMU3B_DEF_TIMEOUT; } pEmu3btn->device = device; pEmu3btn->emulate3Timeout = timeout; Emulate3ButtonsSetEnabled(pEmu3btn, TRUE); } /******************************************************************* * function "Emulate3ButtonsQueueEvent" * * purpose: * Emulate middle button per left/right button events and post events. * argument: * (MouseEmu3btnPtr)pEmu3btn : Emu3btn private record * (int)type : event (ButtonPress / ButtonRelease) * (int)buttons : button (Button1 / Button2 / Button3) * (int)bmask : buttons being pressed (0x1:left / 0x4:right) * *******************************************************************/ void Emulate3ButtonsQueueEvent(MouseEmu3btnPtr pEmu3btn, int type, int buttons, int bmask) { DeviceIntPtr device = pEmu3btn->device; int emulateButtons; int button, flag; ValuatorMask mask; BUG_RETURN_MSG(buttons != ButtonLeft && buttons != ButtonRight, "not left or right button event\n"); if (pEmu3btn->emulate3ButtonsSoft && pEmu3btn->emulate3Pending) buttonTimer(pEmu3btn); if (pEmu3btn->emulate3Buttons && ((bmask & BMASK_MIDDLE) == 0 || Emulate3ButtonsSoft(pEmu3btn))) { const struct button_action *button_action, *timeout_action; /* emulate the third button by the other two */ emulateButtons = BMASKTOINDEX(bmask); button_action = &stateTab[pEmu3btn->emulateState].buttons[emulateButtons]; if ((type = button_action->event1.type) != ButtonNone) { button = button_action->event1.button; flag = POINTER_RELATIVE; valuator_mask_zero(&mask); QueuePointerEvents(device, type, button, flag, &mask); } if ((type = button_action->event2.type) != ButtonNone) { button = button_action->event2.button; flag = POINTER_RELATIVE; valuator_mask_zero(&mask); QueuePointerEvents(device, type, button, flag, &mask); } pEmu3btn->emulateState = button_action->new_state; timeout_action = &stateTab[pEmu3btn->emulateState].timeout; if (timeout_action->event1.type != ButtonNone) { pEmu3btn->emulate3Expires = GetTimeInMillis() + pEmu3btn->emulate3Timeout; pEmu3btn->emulate3Pending = TRUE; } else { pEmu3btn->emulate3Pending = FALSE; } } else { /* no emulation; post left or right button event as is */ flag = POINTER_RELATIVE; valuator_mask_zero(&mask); QueuePointerEvents(device, type, buttons, flag, &mask); } }