2 /* MouseSettings.c- mouse options (some are equivalent to xset)
4 * WPrefs - Window Maker Preferences Program
6 * Copyright (c) 1998-2003 Alfredo K. Kojima
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <X11/Xutil.h>
26 #include <X11/XKBlib.h>
29 #include <sys/types.h>
40 enum { T_BUTTON
, T_WHEEL
} type
;
41 const char *display_label
;
43 { "MouseLeftButtonAction", 3, T_BUTTON
, N_("Left Button") },
44 { "MouseMiddleButtonAction", 2, T_BUTTON
, N_("Middle Button") },
45 { "MouseRightButtonAction", 1, T_BUTTON
, N_("Right Button") },
46 { "MouseBackwardButtonAction", 0, T_BUTTON
, N_("Back Button") },
47 { "MouseForwardButtonAction", 0, T_BUTTON
, N_("Forward Button") },
48 { "MouseWheelAction", 0, T_WHEEL
, N_("Mouse Wheel") },
49 { "MouseWheelTiltAction", 0, T_WHEEL
, N_("Mouse Wheel Tilt") }
55 } button_actions
[] = {
56 { "None", N_("None") },
57 { "OpenApplicationsMenu", N_("Applications Menu") },
58 { "OpenWindowListMenu", N_("Window List Menu") },
59 { "SelectWindows", N_("Select Windows") },
60 { "MoveToPrevWorkspace", N_("Previous Workspace") },
61 { "MoveToNextWorkspace", N_("Next Workspace") },
62 { "MoveToPrevWindow", N_("Previous Window") },
63 { "MoveToNextWindow", N_("Next Window") }
70 { "None", N_("None") },
71 { "SwitchWorkspaces", N_("Switch Workspaces") },
72 { "SwitchWindows", N_("Switch Windows") }
75 typedef struct _Panel
{
82 CallbackRec callbacks
;
103 WMPopUpButton
*popup
;
104 } mouse_action
[wlengthof_nocheck(button_list
)];
109 WMPopUpButton
*grabP
;
111 /**/ int maxThreshold
;
115 #define ICON_FILE "mousesettings"
117 #define SPEED_ICON_FILE "mousespeed"
119 #define DELAY_ICON "timer%i"
120 #define DELAY_ICON_S "timer%is"
122 /* need access to the double click variables */
123 #include <WINGs/WINGsP.h>
125 static char *modifierNames
[8];
127 #define DELAY(i) ((i)*75+170)
130 static void setMouseAccel(WMScreen
* scr
, float accel
, int threshold
)
137 XChangePointerControl(WMScreenDisplay(scr
), True
, True
, n
, d
, threshold
);
140 static void speedChange(WMWidget
* w
, void *data
)
142 _Panel
*panel
= (_Panel
*) data
;
151 tmp
= WMGetTextFieldText(panel
->acceT
);
152 if (sscanf(tmp
, "%f", &accel
) != 1 || accel
< 0) {
153 WMRunAlertPanel(WMWidgetScreen(panel
->acceT
), GetWindow(),
155 _("Invalid mouse acceleration value. Must be a positive real value."),
156 _("OK"), NULL
, NULL
);
160 panel
->acceleration
= accel
;
163 i
= (int)WMGetSliderValue(panel
->speedS
);
165 panel
->acceleration
= 0.25 + (i
* 0.25);
167 sprintf(buffer
, "%.2f", 0.25 + (i
* 0.25));
168 WMSetTextFieldText(panel
->acceT
, buffer
);
171 tmp
= WMGetTextFieldText(panel
->threT
);
172 if (sscanf(tmp
, "%i", &threshold
) != 1 || threshold
< 0 || threshold
> panel
->maxThreshold
) {
173 WMRunAlertPanel(WMWidgetScreen(panel
->parent
), GetWindow(), _("Error"),
175 ("Invalid mouse acceleration threshold value. Must be the number of pixels to travel before accelerating."),
176 _("OK"), NULL
, NULL
);
178 setMouseAccel(WMWidgetScreen(panel
->parent
), panel
->acceleration
, threshold
);
183 static void returnPressed(void *observerData
, WMNotification
* notification
)
185 _Panel
*panel
= (_Panel
*) observerData
;
187 /* Parameter not used, but tell the compiler that it is ok */
190 speedChange(NULL
, panel
);
193 static void toggle_disabling_of_mouse_actions(WMWidget
*w
, void *client_data
)
195 WMButton
*button
= (WMButton
*) w
;
196 Panel
*panel
= (Panel
*) client_data
;
200 if (WMGetButtonSelected(button
))
205 for (i
= 0; i
< wlengthof(panel
->mouse_action
); i
++)
206 WMSetPopUpButtonEnabled(panel
->mouse_action
[i
].popup
, do_enable
);
209 static void doubleClick(WMWidget
* w
, void *data
)
211 _Panel
*panel
= (_Panel
*) data
;
215 for (i
= 0; i
< wlengthof(panel
->ddelaB
); i
++) {
216 if (panel
->ddelaB
[i
] == w
)
219 WINGsConfiguration
.doubleClickDelay
= DELAY(i
);
221 sprintf(buffer
, "%i", DELAY(i
));
222 WMSetTextFieldText(panel
->ddelaT
, buffer
);
225 static int getButtonAction(const char *str
)
232 for (i
= 0; i
< wlengthof(button_actions
); i
++) {
233 if (strcasecmp(str
, button_actions
[i
].db_value
) == 0)
240 static int getWheelAction(const char *str
)
247 for (i
= 0; i
< wlengthof(wheel_actions
); i
++) {
248 if (strcasecmp(str
, wheel_actions
[i
].db_value
) == 0)
255 static void getMouseParameters(Display
* dpy
, float *accel
, int *thre
)
259 XGetPointerControl(dpy
, &n
, &d
, thre
);
261 *accel
= (float)n
/ (float)d
;
264 static void showData(_Panel
* panel
)
271 Display
*dpy
= WMScreenDisplay(WMWidgetScreen(panel
->parent
));
273 for (i
= 0; i
< wlengthof(button_list
); i
++) {
276 str
= GetStringForKey(button_list
[i
].db_key
);
277 if (button_list
[i
].type
== T_BUTTON
)
278 action
= getButtonAction(str
);
280 action
= getWheelAction(str
);
284 wwarning(_("bad value %s for option %s"), str
, button_list
[i
].db_key
);
285 action
= button_list
[i
].default_action
;
287 WMSetPopUpButtonSelectedItem(panel
->mouse_action
[i
].popup
, action
);
290 WMSetButtonSelected(panel
->disaB
, GetBoolForKey("DisableWSMouseActions"));
291 toggle_disabling_of_mouse_actions(panel
->disaB
, panel
);
293 /**/ getMouseParameters(dpy
, &accel
, &a
);
294 panel
->maxThreshold
= WidthOfScreen(DefaultScreenOfDisplay(dpy
));
295 if (a
> panel
->maxThreshold
) {
296 panel
->maxThreshold
= a
;
298 sprintf(buffer
, "%i", a
);
299 WMSetTextFieldText(panel
->threT
, buffer
);
301 WMSetSliderValue(panel
->speedS
, (accel
- 0.25F
) / 0.25F
);
303 panel
->acceleration
= accel
;
304 sprintf(buffer
, "%.2f", (double)accel
);
305 WMSetTextFieldText(panel
->acceT
, buffer
);
307 /**/ b
= GetIntegerForKey("DoubleClickTime");
308 /* find best match */
310 for (i
= 0; i
< wlengthof(panel
->ddelaB
); i
++) {
315 WMPerformButtonClick(panel
->ddelaB
[a
]);
316 sprintf(buffer
, "%i", b
);
317 WMSetTextFieldText(panel
->ddelaT
, buffer
);
319 /**/ str
= GetStringForKey("ModifierKey");
322 a
= ModifierFromKey(dpy
, str
);
325 str
= modifierNames
[a
];
328 for (i
= 0; i
< WMGetPopUpButtonNumberOfItems(panel
->grabP
); i
++) {
329 if (strstr(WMGetPopUpButtonItem(panel
->grabP
, i
), str
)) {
330 WMSetPopUpButtonSelectedItem(panel
->grabP
, i
);
340 previous
= WMGetPopUpButtonItem(panel
->grabP
, 0);
341 if (previous
!= NULL
)
342 WMSetPopUpButtonSelectedItem(panel
->grabP
, 0);
343 wwarning(_("modifier key %s for option ModifierKey was not recognized. Using %s as default"),
344 str
, previous
?previous
:"(empty)");
348 static void fillModifierPopUp(WMPopUpButton
* pop
)
350 XModifierKeymap
*mapping
;
351 Display
*dpy
= WMScreenDisplay(WMWidgetScreen(pop
));
356 mapping
= XGetModifierMapping(dpy
);
358 if (!mapping
|| mapping
->max_keypermod
== 0) {
359 WMAddPopUpButtonItem(pop
, "Mod1");
360 WMAddPopUpButtonItem(pop
, "Mod2");
361 WMAddPopUpButtonItem(pop
, "Mod3");
362 WMAddPopUpButtonItem(pop
, "Mod4");
363 WMAddPopUpButtonItem(pop
, "Mod5");
364 wwarning(_("could not retrieve keyboard modifier mapping"));
368 for (j
= 0; j
< 8; j
++) {
378 memset(array
, 0, sizeof(char *) * 8);
379 for (i
= 0; i
< mapping
->max_keypermod
; i
++) {
380 idx
= i
+ j
* mapping
->max_keypermod
;
381 if (mapping
->modifiermap
[idx
] != 0) {
383 for (l
= 0; l
< 4; l
++) {
384 if (xext_xkb_supported
)
385 ksym
= XkbKeycodeToKeysym(dpy
, mapping
->modifiermap
[idx
], 0, l
);
387 ksym
= XKeycodeToKeysym(dpy
, mapping
->modifiermap
[idx
], 0);
388 if (ksym
!= NoSymbol
)
391 if (ksym
!= NoSymbol
)
392 str
= XKeysymToString(ksym
);
395 if (str
&& !strstr(str
, "_Lock") && !strstr(str
, "Shift")
396 && !strstr(str
, "Control")) {
397 array
[a
++] = wstrdup(str
);
402 for (k
= 0; k
< a
; k
++) {
403 if (array
[k
] == NULL
)
405 tmp
= wstrdup(array
[k
]);
406 ptr
= strstr(tmp
, "_L");
409 ptr
= strstr(tmp
, "_R");
412 sprintf(buffer
, "%s (%s)", modifierNames
[j
], tmp
);
413 /*sprintf(buffer, "%s", tmp); */
414 WMAddPopUpButtonItem(pop
, buffer
);
415 for (i
= k
+ 1; i
< a
; i
++) {
416 if (array
[i
] == NULL
)
418 if (strstr(array
[i
], tmp
)) {
434 XFreeModifiermap(mapping
);
437 static void createPanel(Panel
* p
)
439 _Panel
*panel
= (_Panel
*) p
;
440 WMScreen
*scr
= WMWidgetScreen(panel
->parent
);
451 panel
->box
= WMCreateBox(panel
->parent
);
452 WMSetViewExpandsToParent(WMWidgetView(panel
->box
), 2, 2, 2, 2);
454 /**************** Mouse Speed ****************/
455 panel
->speedF
= WMCreateFrame(panel
->box
);
456 WMResizeWidget(panel
->speedF
, 219, 85);
457 WMMoveWidget(panel
->speedF
, 9, 54);
458 WMSetFrameTitle(panel
->speedF
, _("Mouse Speed"));
460 panel
->speedL
= WMCreateLabel(panel
->speedF
);
461 WMResizeWidget(panel
->speedL
, 40, 46);
462 WMMoveWidget(panel
->speedL
, 8, 10);
463 WMSetLabelImagePosition(panel
->speedL
, WIPImageOnly
);
464 path
= LocateImage(SPEED_ICON_FILE
);
466 icon
= WMCreateBlendedPixmapFromFile(scr
, path
, &color
);
468 WMSetLabelImage(panel
->speedL
, icon
);
469 WMReleasePixmap(icon
);
471 wwarning(_("could not load icon %s"), path
);
476 panel
->speedS
= WMCreateSlider(panel
->speedF
);
477 WMResizeWidget(panel
->speedS
, 150, 15);
478 WMMoveWidget(panel
->speedS
, 58, 30);
479 WMSetSliderMinValue(panel
->speedS
, 0);
480 WMSetSliderMaxValue(panel
->speedS
, 40);
481 WMSetSliderContinuous(panel
->speedS
, False
);
482 WMSetSliderAction(panel
->speedS
, speedChange
, panel
);
484 panel
->acceL
= WMCreateLabel(panel
->speedF
);
485 WMResizeWidget(panel
->acceL
, 50, 16);
486 WMMoveWidget(panel
->acceL
, 8, 58);
487 WMSetLabelTextAlignment(panel
->acceL
, WARight
);
488 WMSetLabelText(panel
->acceL
, _("Accel.:"));
490 panel
->acceT
= WMCreateTextField(panel
->speedF
);
491 WMResizeWidget(panel
->acceT
, 40, 20);
492 WMMoveWidget(panel
->acceT
, 58, 56);
493 WMAddNotificationObserver(returnPressed
, panel
, WMTextDidEndEditingNotification
, panel
->acceT
);
495 panel
->threL
= WMCreateLabel(panel
->speedF
);
496 WMResizeWidget(panel
->threL
, 80, 16);
497 WMMoveWidget(panel
->threL
, 98, 58);
498 WMSetLabelTextAlignment(panel
->threL
, WARight
);
499 WMSetLabelText(panel
->threL
, _("Threshold:"));
501 panel
->threT
= WMCreateTextField(panel
->speedF
);
502 WMResizeWidget(panel
->threT
, 30, 20);
503 WMMoveWidget(panel
->threT
, 178, 56);
504 WMAddNotificationObserver(returnPressed
, panel
, WMTextDidEndEditingNotification
, panel
->threT
);
506 WMMapSubwidgets(panel
->speedF
);
508 /* ************** Grab Modifier **************** */
509 panel
->grabF
= WMCreateFrame(panel
->box
);
510 WMResizeWidget(panel
->grabF
, 219, 46);
511 WMMoveWidget(panel
->grabF
, 9, 5);
512 WMSetFrameTitle(panel
->grabF
, _("Mouse Grab Modifier"));
514 WMSetBalloonTextForView(_("Keyboard modifier to use for actions that\n"
515 "involve dragging windows with the mouse,\n"
516 "clicking inside the window."), WMWidgetView(panel
->grabF
));
518 panel
->grabP
= WMCreatePopUpButton(panel
->grabF
);
519 WMResizeWidget(panel
->grabP
, 178, 20);
520 WMMoveWidget(panel
->grabP
, 20, 17);
522 fillModifierPopUp(panel
->grabP
);
523 WMMapSubwidgets(panel
->grabF
);
525 /***************** Doubleclick Delay ****************/
527 panel
->ddelaF
= WMCreateFrame(panel
->box
);
528 WMResizeWidget(panel
->ddelaF
, 219, 80);
529 WMMoveWidget(panel
->ddelaF
, 9, 142);
530 WMSetFrameTitle(panel
->ddelaF
, _("Double-Click Delay"));
532 buf1
= wmalloc(strlen(DELAY_ICON
) + 2);
533 buf2
= wmalloc(strlen(DELAY_ICON_S
) + 2);
535 for (i
= 0; i
< wlengthof(panel
->ddelaB
); i
++) {
536 panel
->ddelaB
[i
] = WMCreateCustomButton(panel
->ddelaF
, WBBStateChangeMask
);
537 WMResizeWidget(panel
->ddelaB
[i
], 25, 25);
538 WMMoveWidget(panel
->ddelaB
[i
], 18 + (40 * i
), 18);
539 WMSetButtonBordered(panel
->ddelaB
[i
], False
);
540 WMSetButtonImagePosition(panel
->ddelaB
[i
], WIPImageOnly
);
541 WMSetButtonAction(panel
->ddelaB
[i
], doubleClick
, panel
);
543 WMGroupButtons(panel
->ddelaB
[0], panel
->ddelaB
[i
]);
545 sprintf(buf1
, DELAY_ICON
, i
+ 1);
546 sprintf(buf2
, DELAY_ICON_S
, i
+ 1);
547 path
= LocateImage(buf1
);
549 icon
= WMCreatePixmapFromFile(scr
, path
);
551 WMSetButtonImage(panel
->ddelaB
[i
], icon
);
552 WMReleasePixmap(icon
);
554 wwarning(_("could not load icon file %s"), path
);
558 path
= LocateImage(buf2
);
560 icon
= WMCreatePixmapFromFile(scr
, path
);
562 WMSetButtonAltImage(panel
->ddelaB
[i
], icon
);
563 WMReleasePixmap(icon
);
565 wwarning(_("could not load icon file %s"), path
);
573 panel
->tester
= CreateDoubleTest(panel
->ddelaF
, _("Test"));
574 WMResizeWidget(panel
->tester
, 84, 24);
575 WMMoveWidget(panel
->tester
, 20, 48);
577 panel
->ddelaT
= WMCreateTextField(panel
->ddelaF
);
578 WMResizeWidget(panel
->ddelaT
, 40, 20);
579 WMMoveWidget(panel
->ddelaT
, 130, 50);
581 panel
->ddelaL
= WMCreateLabel(panel
->ddelaF
);
582 WMResizeWidget(panel
->ddelaL
, 40, 16);
583 WMMoveWidget(panel
->ddelaL
, 173, 54);
588 font
= WMSystemFontOfSize(scr
, 10);
589 color
= WMDarkGrayColor(scr
);
590 WMSetLabelTextColor(panel
->ddelaL
, color
);
591 WMSetLabelFont(panel
->ddelaL
, font
);
593 WMReleaseColor(color
);
595 WMSetLabelText(panel
->ddelaL
, _("ms"));
597 WMMapSubwidgets(panel
->ddelaF
);
599 /* ************** Workspace Action Buttons **************** */
601 panel
->menuF
= WMCreateFrame(panel
->box
);
602 WMResizeWidget(panel
->menuF
, 276, 217);
603 WMMoveWidget(panel
->menuF
, 236, 5);
604 WMSetFrameTitle(panel
->menuF
, _("Workspace Mouse Actions"));
606 panel
->disaB
= WMCreateSwitchButton(panel
->menuF
);
607 WMResizeWidget(panel
->disaB
, 254, 18);
608 WMMoveWidget(panel
->disaB
, 10, 15);
609 WMSetButtonText(panel
->disaB
, _("Disable mouse actions"));
610 WMSetButtonAction(panel
->disaB
, toggle_disabling_of_mouse_actions
, panel
);
612 for (i
= 0; i
< wlengthof(button_list
); i
++) {
615 panel
->mouse_action
[i
].label
= WMCreateLabel(panel
->menuF
);
616 WMResizeWidget(panel
->mouse_action
[i
].label
, 115, 20);
617 WMMoveWidget(panel
->mouse_action
[i
].label
, 4, 37 + 25 * i
);
618 WMSetLabelTextAlignment(panel
->mouse_action
[i
].label
, WARight
);
619 WMSetLabelText(panel
->mouse_action
[i
].label
, _(button_list
[i
].display_label
));
621 panel
->mouse_action
[i
].popup
= WMCreatePopUpButton(panel
->menuF
);
622 WMResizeWidget(panel
->mouse_action
[i
].popup
, 145, 20);
623 WMMoveWidget(panel
->mouse_action
[i
].popup
, 121, 37 + 25 * i
);
625 if (button_list
[i
].type
== T_BUTTON
) {
626 for (j
= 0; j
< wlengthof(button_actions
); j
++)
627 WMAddPopUpButtonItem(panel
->mouse_action
[i
].popup
, _(button_actions
[j
].label
));
629 for (j
= 0; j
< wlengthof(wheel_actions
); j
++)
630 WMAddPopUpButtonItem(panel
->mouse_action
[i
].popup
, _(wheel_actions
[j
].label
));
633 WMMapSubwidgets(panel
->menuF
);
635 WMRealizeWidget(panel
->box
);
636 WMMapSubwidgets(panel
->box
);
641 static void storeCommandInScript(const char *cmd
, const char *line
)
648 /* Calculate permission to be Executable but taking into account user's umask */
649 permissions
= umask(0);
651 permissions
= (S_IRWXU
| S_IRWXG
| S_IRWXO
) & (~permissions
);
653 path
= wstrconcat(wusergnusteppath(), "/Library/WindowMaker/autostart");
655 f
= fopen(path
, "rb");
657 f
= fopen(path
, "wb");
659 werror(_("could not create %s"), path
);
662 fprintf(f
, "#!/bin/sh\n");
666 int len
= strlen(cmd
);
671 tmppath
= wstrconcat(wusergnusteppath(), "/Library/WindowMaker/autostart.tmp");
672 fo
= fopen(tmppath
, "wb");
674 werror(_("could not create temporary file %s"), tmppath
);
680 if (!fgets(buffer
, 127, f
)) {
683 if (buffer
[0] == '\n') {
684 /* don't write empty lines, else the file will grow
685 * indefinitely (one '\n' added at end of file on each save).
689 if (strncmp(buffer
, cmd
, len
) == 0) {
706 if (rename(tmppath
, path
) != 0) {
707 werror(_("could not rename file %s to %s"), tmppath
, path
);
711 if (chmod(path
, permissions
) != 0)
712 wwarning(_("could not set permission 0%03o on file \"%s\""), permissions
, path
);
717 fsync(fileno(f
)); /* this may be rw */
722 static void storeData(_Panel
* panel
)
727 WMUserDefaults
*udb
= WMGetStandardUserDefaults();
729 if (!WMGetUDBoolForKey(udb
, "NoXSetStuff")) {
730 tmp
= WMGetTextFieldText(panel
->threT
);
731 if (strlen(tmp
) == 0) {
736 sprintf(buffer
, XSET
" m %i/%i %s\n", (int)(panel
->acceleration
* 10), 10, tmp
);
737 storeCommandInScript(XSET
" m", buffer
);
742 tmp
= WMGetTextFieldText(panel
->ddelaT
);
743 if (sscanf(tmp
, "%i", &i
) == 1 && i
> 0)
744 SetIntegerForKey(i
, "DoubleClickTime");
747 SetBoolForKey(WMGetButtonSelected(panel
->disaB
), "DisableWSMouseActions");
749 for (i
= 0; i
< wlengthof(button_list
); i
++) {
750 const char *db_value
;
753 action
= WMGetPopUpButtonSelectedItem(panel
->mouse_action
[i
].popup
);
754 if (button_list
[i
].type
== T_BUTTON
)
755 db_value
= button_actions
[action
].db_value
;
757 db_value
= wheel_actions
[action
].db_value
;
758 SetStringForKey(db_value
, button_list
[i
].db_key
);
761 tmp
= WMGetPopUpButtonItem(panel
->grabP
, WMGetPopUpButtonSelectedItem(panel
->grabP
));
763 p
= strchr(tmp
, ' ');
767 SetStringForKey(tmp
, "ModifierKey");
772 Panel
*InitMouseSettings(WMWidget
*parent
)
776 modifierNames
[0] = wstrdup(_("Shift"));
777 modifierNames
[1] = wstrdup(_("Lock"));
778 modifierNames
[2] = wstrdup(_("Control"));
779 modifierNames
[3] = wstrdup(_("Mod1"));
780 modifierNames
[4] = wstrdup(_("Mod2"));
781 modifierNames
[5] = wstrdup(_("Mod3"));
782 modifierNames
[6] = wstrdup(_("Mod4"));
783 modifierNames
[7] = wstrdup(_("Mod5"));
785 panel
= wmalloc(sizeof(_Panel
));
787 panel
->sectionName
= _("Mouse Preferences");
789 panel
->description
= _("Mouse speed/acceleration, double click delay,\n" "mouse button bindings etc.");
791 panel
->parent
= parent
;
793 panel
->callbacks
.createWidgets
= createPanel
;
794 panel
->callbacks
.updateDomain
= storeData
;
796 AddSection(panel
, ICON_FILE
);