wmix: added function to handle Multimedia keys related to volume
We can handle the keys AudioRaiseVolume, AudioLowerVolume and AudioMute, so we ask the X server to send the key press event for them to us and update the volume appropriately. Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
This commit is contained in:
		
							parent
							
								
									3e2d8a730a
								
							
						
					
					
						commit
						4379368290
					
				
					 6 changed files with 251 additions and 2 deletions
				
			
		| 
						 | 
					@ -2,7 +2,7 @@ CC		= gcc
 | 
				
			||||||
CFLAGS		= -O3 -W -Wall
 | 
					CFLAGS		= -O3 -W -Wall
 | 
				
			||||||
LDFLAGS		= -L/usr/X11R6/lib
 | 
					LDFLAGS		= -L/usr/X11R6/lib
 | 
				
			||||||
LIBS		= -lXpm -lXext -lX11 -lm
 | 
					LIBS		= -lXpm -lXext -lX11 -lm
 | 
				
			||||||
OBJECTS		= misc.o config.o mixer-oss.o ui_x.o wmix.o
 | 
					OBJECTS		= misc.o config.o mixer-oss.o ui_x.o mmkeys.o wmix.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# where to install this program (also for packaging stuff)
 | 
					# where to install this program (also for packaging stuff)
 | 
				
			||||||
PREFIX		= /usr/local
 | 
					PREFIX		= /usr/local
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,3 +33,11 @@ typedef unsigned int bool;
 | 
				
			||||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 | 
					#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_DOUBLE_CLICK_TIME 0.5
 | 
					#define MAX_DOUBLE_CLICK_TIME 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Get the number of element in a static array
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Do not use on an allocated array, it will not work
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define lengthof(arr)  \
 | 
				
			||||||
 | 
						((ssize_t)(sizeof( arr ) / sizeof( arr[0] )))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								wmix/include/mmkeys.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								wmix/include/mmkeys.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					/* WMix -- a mixer using the OSS mixer API
 | 
				
			||||||
 | 
					 * Copyright (C)2014 Christophe CURIS for the WindowMaker Team
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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/mmkeys.h: functions related to handling Multimedia keys */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef WMIX_MMKEYS_H
 | 
				
			||||||
 | 
					#define WMIX_MMKEYS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Global Configuration */
 | 
				
			||||||
 | 
					extern struct multimedia_keys {
 | 
				
			||||||
 | 
						KeyCode raise_volume;
 | 
				
			||||||
 | 
						KeyCode lower_volume;
 | 
				
			||||||
 | 
						KeyCode mute;
 | 
				
			||||||
 | 
					} mmkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Grab the multimedia keys */
 | 
				
			||||||
 | 
					void mmkey_install(Display *display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif	/* WMIX_MMKEYS_H */
 | 
				
			||||||
							
								
								
									
										168
									
								
								wmix/mmkeys.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								wmix/mmkeys.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,168 @@
 | 
				
			||||||
 | 
					/* WMix -- a mixer using the OSS mixer API.
 | 
				
			||||||
 | 
					 * Copyright (C) 2014 Christophe CURIS for the WindowMaker Team
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * mmkeys.c: functions related to grabing the Multimedia Keys on keyboard
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <X11/Xlib.h>
 | 
				
			||||||
 | 
					#include <X11/keysym.h>
 | 
				
			||||||
 | 
					#include <X11/XF86keysym.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "include/common.h"
 | 
				
			||||||
 | 
					#include "include/config.h"
 | 
				
			||||||
 | 
					#include "include/mmkeys.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The global configuration */
 | 
				
			||||||
 | 
					struct multimedia_keys mmkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The list of keys we're interrested in */
 | 
				
			||||||
 | 
					static const struct {
 | 
				
			||||||
 | 
						KeySym  symbol;
 | 
				
			||||||
 | 
						KeyCode *store;
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
					} key_list[] = {
 | 
				
			||||||
 | 
						{ XF86XK_AudioRaiseVolume, &mmkeys.raise_volume, "AudioRaiseVolume" },
 | 
				
			||||||
 | 
						{ XF86XK_AudioLowerVolume, &mmkeys.lower_volume, "AudioLowerVolume" },
 | 
				
			||||||
 | 
						{ XF86XK_AudioMute,        &mmkeys.mute,         "AudioMute"        }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The modifiers that should not have impact on the key grabbed */
 | 
				
			||||||
 | 
					static const struct {
 | 
				
			||||||
 | 
						KeySym symbol;
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
					} modifier_symbol[] = {
 | 
				
			||||||
 | 
						{ XK_Caps_Lock, "CapsLock" },
 | 
				
			||||||
 | 
						{ XK_Num_Lock,  "NumLock"  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						int count;
 | 
				
			||||||
 | 
						unsigned int list[1 << lengthof(modifier_symbol)];
 | 
				
			||||||
 | 
					} modifier_masks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Local functions */
 | 
				
			||||||
 | 
					static void mmkey_build_modifier_list(Display *display, modifier_masks *result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Grab the multimedia keys on the X server
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * That basically means that whenever these keys are pressed
 | 
				
			||||||
 | 
					 * the events will be sent to us instead of the application
 | 
				
			||||||
 | 
					 * that has current focus.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void mmkey_install(Display *display)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						modifier_masks mod_masks;
 | 
				
			||||||
 | 
						Window root_window;
 | 
				
			||||||
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mmkey_build_modifier_list(display, &mod_masks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						root_window = DefaultRootWindow(display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < lengthof(key_list); i++) {
 | 
				
			||||||
 | 
							KeyCode key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key = XKeysymToKeycode(display, key_list[i].symbol);
 | 
				
			||||||
 | 
							*(key_list[i].store) = key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (key == None)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (j = 0; j < mod_masks.count; j++) {
 | 
				
			||||||
 | 
								XGrabKey(display, key, mod_masks.list[j], root_window,
 | 
				
			||||||
 | 
								         False, GrabModeAsync, GrabModeAsync);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (config.verbose)
 | 
				
			||||||
 | 
								printf("Found multimedia key: %s\n", key_list[i].name);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Build the list of bit-masks for all the modifiers we want to not have impact on our grab
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void mmkey_build_modifier_list(Display *display, modifier_masks *result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						XModifierKeymap *mods;
 | 
				
			||||||
 | 
						KeyCode mod_code[lengthof(modifier_symbol)];
 | 
				
			||||||
 | 
						unsigned int mod_mask[lengthof(modifier_symbol)];
 | 
				
			||||||
 | 
						char buffer[256];
 | 
				
			||||||
 | 
						int nb_modifiers;
 | 
				
			||||||
 | 
						int i, j, k;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Get the bitmask associated with the modifiers */
 | 
				
			||||||
 | 
						for (i = 0; i < lengthof(modifier_symbol); i++) {
 | 
				
			||||||
 | 
							mod_code[i] = XKeysymToKeycode(display, modifier_symbol[i].symbol);
 | 
				
			||||||
 | 
							mod_mask[i] = 0L;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mods = XGetModifierMapping(display);
 | 
				
			||||||
 | 
						for (i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
							for (j = 0; j < mods->max_keypermod; j++) {
 | 
				
			||||||
 | 
								KeyCode key_mod;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key_mod = mods->modifiermap[i * mods->max_keypermod + j];
 | 
				
			||||||
 | 
								for (k = 0; k < lengthof(mod_code); k++) {
 | 
				
			||||||
 | 
									if ((mod_code[k] != None) && (key_mod == mod_code[k]))
 | 
				
			||||||
 | 
										mod_mask[k] |= 1 << i;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						XFreeModifiermap(mods);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Count the number of modifiers found (and display the list to the user) */
 | 
				
			||||||
 | 
						if (config.verbose)
 | 
				
			||||||
 | 
							strcpy(buffer, "Found key modifiers: ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nb_modifiers = 0;
 | 
				
			||||||
 | 
						for (i = 0; i < lengthof(modifier_symbol); i++) {
 | 
				
			||||||
 | 
							if (mod_mask[i] != 0) {
 | 
				
			||||||
 | 
								if (config.verbose) {
 | 
				
			||||||
 | 
									if (nb_modifiers > 0)
 | 
				
			||||||
 | 
										strcat(buffer, ", ");
 | 
				
			||||||
 | 
									strcat(buffer, modifier_symbol[i].name);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								nb_modifiers++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (config.verbose) {
 | 
				
			||||||
 | 
							if (nb_modifiers == 0)
 | 
				
			||||||
 | 
								strcat(buffer, "None");
 | 
				
			||||||
 | 
							puts(buffer);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Build the list of possible combinations of modifiers */
 | 
				
			||||||
 | 
						result->count = 1 << nb_modifiers;
 | 
				
			||||||
 | 
						for (i = 0; i < lengthof(result->list); i++)
 | 
				
			||||||
 | 
							result->list[i] = 0L;
 | 
				
			||||||
 | 
						k = 1;
 | 
				
			||||||
 | 
						for (i = 0; i < lengthof(mod_mask); i++) {
 | 
				
			||||||
 | 
							if (mod_mask[i] != 0) {
 | 
				
			||||||
 | 
								for (j = 1; j < result->count; j++)
 | 
				
			||||||
 | 
									if (j & k)
 | 
				
			||||||
 | 
										result->list[j] |= mod_mask[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								k <<= 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,8 @@ Allows toggling record source,
 | 
				
			||||||
muting individual channels, adjusting volume and balance, all in a
 | 
					muting individual channels, adjusting volume and balance, all in a
 | 
				
			||||||
compact dockapp size, with TV\-like on\-screen\-display for volume levels.
 | 
					compact dockapp size, with TV\-like on\-screen\-display for volume levels.
 | 
				
			||||||
.LP
 | 
					.LP
 | 
				
			||||||
Supports mouse wheel to adjust current channel's volume
 | 
					Supports mouse wheel to adjust current channel's volume,
 | 
				
			||||||
 | 
					supports also the volume control keys on \(lqmultimedia\(rq keyboards
 | 
				
			||||||
and can be controlled remotely with unix signals
 | 
					and can be controlled remotely with unix signals
 | 
				
			||||||
.IR SIGUSR1 / SIGUSR2
 | 
					.IR SIGUSR1 / SIGUSR2
 | 
				
			||||||
to raise/lower the volume.
 | 
					to raise/lower the volume.
 | 
				
			||||||
| 
						 | 
					@ -144,3 +145,4 @@ It was expanded by Christophe CURIS for the Window Maker Dev Team.
 | 
				
			||||||
.PP
 | 
					.PP
 | 
				
			||||||
wmix was written by Tim, timecop <timecop@japan.co.jp>,
 | 
					wmix was written by Tim, timecop <timecop@japan.co.jp>,
 | 
				
			||||||
with some code by Daniel Richard G. <skunk@mit.edu>
 | 
					with some code by Daniel Richard G. <skunk@mit.edu>
 | 
				
			||||||
 | 
					and some addition by Christophe CURIS.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										37
									
								
								wmix/wmix.c
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								wmix/wmix.c
									
									
									
									
									
								
							| 
						 | 
					@ -37,6 +37,7 @@
 | 
				
			||||||
#include "include/mixer.h"
 | 
					#include "include/mixer.h"
 | 
				
			||||||
#include "include/misc.h"
 | 
					#include "include/misc.h"
 | 
				
			||||||
#include "include/ui_x.h"
 | 
					#include "include/ui_x.h"
 | 
				
			||||||
 | 
					#include "include/mmkeys.h"
 | 
				
			||||||
#include "include/config.h"
 | 
					#include "include/config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +56,7 @@ static int idle_loop;
 | 
				
			||||||
static void signal_catch(int sig);
 | 
					static void signal_catch(int sig);
 | 
				
			||||||
static void button_press_event(XButtonEvent *event);
 | 
					static void button_press_event(XButtonEvent *event);
 | 
				
			||||||
static void button_release_event(XButtonEvent *event);
 | 
					static void button_release_event(XButtonEvent *event);
 | 
				
			||||||
 | 
					static int  key_press_event(XKeyEvent *event);
 | 
				
			||||||
static void motion_event(XMotionEvent *event);
 | 
					static void motion_event(XMotionEvent *event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +93,7 @@ int main(int argc, char **argv)
 | 
				
			||||||
    dockapp_init(display);
 | 
					    dockapp_init(display);
 | 
				
			||||||
    new_window("wmix", 64, 64);
 | 
					    new_window("wmix", 64, 64);
 | 
				
			||||||
    new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60);
 | 
					    new_osd(DisplayWidth(display, DefaultScreen(display)) - 200, 60);
 | 
				
			||||||
 | 
					    mmkey_install(display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config_release();
 | 
					    config_release();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,6 +119,10 @@ int main(int argc, char **argv)
 | 
				
			||||||
	if (button_pressed || slider_pressed || (XPending(display) > 0)) {
 | 
						if (button_pressed || slider_pressed || (XPending(display) > 0)) {
 | 
				
			||||||
	    XNextEvent(display, &event);
 | 
						    XNextEvent(display, &event);
 | 
				
			||||||
	    switch (event.type) {
 | 
						    switch (event.type) {
 | 
				
			||||||
 | 
							case KeyPress:
 | 
				
			||||||
 | 
							    if (key_press_event(&event.xkey))
 | 
				
			||||||
 | 
								idle_loop = 0;
 | 
				
			||||||
 | 
							    break;
 | 
				
			||||||
		case Expose:
 | 
							case Expose:
 | 
				
			||||||
		    redraw_window();
 | 
							    redraw_window();
 | 
				
			||||||
		    break;
 | 
							    break;
 | 
				
			||||||
| 
						 | 
					@ -276,6 +283,36 @@ static void button_press_event(XButtonEvent *event)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int key_press_event(XKeyEvent *event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (event->keycode == mmkeys.raise_volume) {
 | 
				
			||||||
 | 
							mixer_set_volume_rel(config.scrollstep);
 | 
				
			||||||
 | 
							if (!osd_mapped())
 | 
				
			||||||
 | 
								map_osd();
 | 
				
			||||||
 | 
							if (osd_mapped())
 | 
				
			||||||
 | 
								update_osd(mixer_get_volume(), false);
 | 
				
			||||||
 | 
							ui_update();
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (event->keycode == mmkeys.lower_volume) {
 | 
				
			||||||
 | 
							mixer_set_volume_rel(-config.scrollstep);
 | 
				
			||||||
 | 
							if (!osd_mapped())
 | 
				
			||||||
 | 
								map_osd();
 | 
				
			||||||
 | 
							if (osd_mapped())
 | 
				
			||||||
 | 
								update_osd(mixer_get_volume(), false);
 | 
				
			||||||
 | 
							ui_update();
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (event->keycode == mmkeys.mute) {
 | 
				
			||||||
 | 
							mixer_toggle_mute();
 | 
				
			||||||
 | 
							ui_update();
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Ignore other keys */
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void button_release_event(XButtonEvent *event)
 | 
					static void button_release_event(XButtonEvent *event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int x = event->x;
 | 
					    int x = event->x;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue