xref: /OK3568_Linux_fs/external/xserver/hw/xwin/winmouse.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *Permission is hereby granted, free of charge, to any person obtaining
5*4882a593Smuzhiyun  * a copy of this software and associated documentation files (the
6*4882a593Smuzhiyun  *"Software"), to deal in the Software without restriction, including
7*4882a593Smuzhiyun  *without limitation the rights to use, copy, modify, merge, publish,
8*4882a593Smuzhiyun  *distribute, sublicense, and/or sell copies of the Software, and to
9*4882a593Smuzhiyun  *permit persons to whom the Software is furnished to do so, subject to
10*4882a593Smuzhiyun  *the following conditions:
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  *The above copyright notice and this permission notice shall be
13*4882a593Smuzhiyun  *included in all copies or substantial portions of the Software.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16*4882a593Smuzhiyun  *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17*4882a593Smuzhiyun  *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18*4882a593Smuzhiyun  *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19*4882a593Smuzhiyun  *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20*4882a593Smuzhiyun  *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21*4882a593Smuzhiyun  *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  *Except as contained in this notice, the name of the XFree86 Project
24*4882a593Smuzhiyun  *shall not be used in advertising or otherwise to promote the sale, use
25*4882a593Smuzhiyun  *or other dealings in this Software without prior written authorization
26*4882a593Smuzhiyun  *from the XFree86 Project.
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * Authors:	Dakshinamurthy Karra
29*4882a593Smuzhiyun  *		Suhaib M Siddiqi
30*4882a593Smuzhiyun  *		Peter Busch
31*4882a593Smuzhiyun  *		Harold L Hunt II
32*4882a593Smuzhiyun  */
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #ifdef HAVE_XWIN_CONFIG_H
35*4882a593Smuzhiyun #include <xwin-config.h>
36*4882a593Smuzhiyun #endif
37*4882a593Smuzhiyun #include "win.h"
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #include "inputstr.h"
40*4882a593Smuzhiyun #include "exevents.h"           /* for button/axes labels */
41*4882a593Smuzhiyun #include "xserver-properties.h"
42*4882a593Smuzhiyun #include "inpututils.h"
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* Peek the internal button mapping */
45*4882a593Smuzhiyun static CARD8 const *g_winMouseButtonMap = NULL;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun /*
48*4882a593Smuzhiyun  * Local prototypes
49*4882a593Smuzhiyun  */
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun static void
52*4882a593Smuzhiyun  winMouseCtrl(DeviceIntPtr pDevice, PtrCtrl * pCtrl);
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun static void
winMouseCtrl(DeviceIntPtr pDevice,PtrCtrl * pCtrl)55*4882a593Smuzhiyun winMouseCtrl(DeviceIntPtr pDevice, PtrCtrl * pCtrl)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun /*
60*4882a593Smuzhiyun  * See Porting Layer Definition - p. 18
61*4882a593Smuzhiyun  * This is known as a DeviceProc
62*4882a593Smuzhiyun  */
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun int
winMouseProc(DeviceIntPtr pDeviceInt,int iState)65*4882a593Smuzhiyun winMouseProc(DeviceIntPtr pDeviceInt, int iState)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun     int lngMouseButtons, i;
68*4882a593Smuzhiyun     int lngWheelEvents = 4;
69*4882a593Smuzhiyun     CARD8 *map;
70*4882a593Smuzhiyun     DevicePtr pDevice = (DevicePtr) pDeviceInt;
71*4882a593Smuzhiyun     Atom btn_labels[9];
72*4882a593Smuzhiyun     Atom axes_labels[2];
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun     switch (iState) {
75*4882a593Smuzhiyun     case DEVICE_INIT:
76*4882a593Smuzhiyun         /* Get number of mouse buttons */
77*4882a593Smuzhiyun         lngMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
78*4882a593Smuzhiyun         winMsg(X_PROBED, "%d mouse buttons found\n", lngMouseButtons);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun         /* Mapping of windows events to X events:
81*4882a593Smuzhiyun          * LEFT:1 MIDDLE:2 RIGHT:3
82*4882a593Smuzhiyun          * SCROLL_UP:4 SCROLL_DOWN:5
83*4882a593Smuzhiyun          * TILT_LEFT:6 TILT_RIGHT:7
84*4882a593Smuzhiyun          * XBUTTON 1:8 XBUTTON 2:9 (most commonly 'back' and 'forward')
85*4882a593Smuzhiyun          * ...
86*4882a593Smuzhiyun          *
87*4882a593Smuzhiyun          * The current Windows API only defines 2 extra buttons, so we don't
88*4882a593Smuzhiyun          * expect more than 5 buttons to be reported, but more than that
89*4882a593Smuzhiyun          * should be handled correctly
90*4882a593Smuzhiyun          */
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun         /*
93*4882a593Smuzhiyun          * To map scroll wheel correctly we need at least the 3 normal buttons
94*4882a593Smuzhiyun          */
95*4882a593Smuzhiyun         if (lngMouseButtons < 3)
96*4882a593Smuzhiyun             lngMouseButtons = 3;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun         /* allocate memory:
99*4882a593Smuzhiyun          * number of buttons + 4 x mouse wheel event + 1 extra (offset for map)
100*4882a593Smuzhiyun          */
101*4882a593Smuzhiyun         map = malloc(sizeof(CARD8) * (lngMouseButtons + lngWheelEvents + 1));
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun         /* initalize button map */
104*4882a593Smuzhiyun         map[0] = 0;
105*4882a593Smuzhiyun         for (i = 1; i <= lngMouseButtons + lngWheelEvents; i++)
106*4882a593Smuzhiyun             map[i] = i;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun         btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
109*4882a593Smuzhiyun         btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
110*4882a593Smuzhiyun         btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
111*4882a593Smuzhiyun         btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
112*4882a593Smuzhiyun         btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
113*4882a593Smuzhiyun         btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
114*4882a593Smuzhiyun         btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
115*4882a593Smuzhiyun         btn_labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_BACK);
116*4882a593Smuzhiyun         btn_labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_FORWARD);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun         axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
119*4882a593Smuzhiyun         axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun         InitPointerDeviceStruct(pDevice,
122*4882a593Smuzhiyun                                 map,
123*4882a593Smuzhiyun                                 lngMouseButtons + lngWheelEvents,
124*4882a593Smuzhiyun                                 btn_labels,
125*4882a593Smuzhiyun                                 winMouseCtrl,
126*4882a593Smuzhiyun                                 GetMotionHistorySize(), 2, axes_labels);
127*4882a593Smuzhiyun         free(map);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun         g_winMouseButtonMap = pDeviceInt->button->map;
130*4882a593Smuzhiyun         break;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun     case DEVICE_ON:
133*4882a593Smuzhiyun         pDevice->on = TRUE;
134*4882a593Smuzhiyun         break;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun     case DEVICE_CLOSE:
137*4882a593Smuzhiyun         g_winMouseButtonMap = NULL;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun     case DEVICE_OFF:
140*4882a593Smuzhiyun         pDevice->on = FALSE;
141*4882a593Smuzhiyun         break;
142*4882a593Smuzhiyun     }
143*4882a593Smuzhiyun     return Success;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun /* Handle the mouse wheel */
147*4882a593Smuzhiyun int
winMouseWheel(int * iTotalDeltaZ,int iDeltaZ,int iButtonUp,int iButtonDown)148*4882a593Smuzhiyun winMouseWheel(int *iTotalDeltaZ, int iDeltaZ, int iButtonUp, int iButtonDown)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun     int button;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun     /* Do we have any previous delta stored? */
153*4882a593Smuzhiyun     if ((*iTotalDeltaZ > 0 && iDeltaZ > 0)
154*4882a593Smuzhiyun         || (*iTotalDeltaZ < 0 && iDeltaZ < 0)) {
155*4882a593Smuzhiyun         /* Previous delta and of same sign as current delta */
156*4882a593Smuzhiyun         iDeltaZ += *iTotalDeltaZ;
157*4882a593Smuzhiyun         *iTotalDeltaZ = 0;
158*4882a593Smuzhiyun     }
159*4882a593Smuzhiyun     else {
160*4882a593Smuzhiyun         /*
161*4882a593Smuzhiyun          * Previous delta of different sign, or zero.
162*4882a593Smuzhiyun          * We will set it to zero for either case,
163*4882a593Smuzhiyun          * as blindly setting takes just as much time
164*4882a593Smuzhiyun          * as checking, then setting if necessary :)
165*4882a593Smuzhiyun          */
166*4882a593Smuzhiyun         *iTotalDeltaZ = 0;
167*4882a593Smuzhiyun     }
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun     /*
170*4882a593Smuzhiyun      * Only process this message if the wheel has moved further than
171*4882a593Smuzhiyun      * WHEEL_DELTA
172*4882a593Smuzhiyun      */
173*4882a593Smuzhiyun     if (iDeltaZ >= WHEEL_DELTA || (-1 * iDeltaZ) >= WHEEL_DELTA) {
174*4882a593Smuzhiyun         *iTotalDeltaZ = 0;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun         /* Figure out how many whole deltas of the wheel we have */
177*4882a593Smuzhiyun         iDeltaZ /= WHEEL_DELTA;
178*4882a593Smuzhiyun     }
179*4882a593Smuzhiyun     else {
180*4882a593Smuzhiyun         /*
181*4882a593Smuzhiyun          * Wheel has not moved past WHEEL_DELTA threshold;
182*4882a593Smuzhiyun          * we will store the wheel delta until the threshold
183*4882a593Smuzhiyun          * has been reached.
184*4882a593Smuzhiyun          */
185*4882a593Smuzhiyun         *iTotalDeltaZ = iDeltaZ;
186*4882a593Smuzhiyun         return 0;
187*4882a593Smuzhiyun     }
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun     /* Set the button to indicate up or down wheel delta */
190*4882a593Smuzhiyun     if (iDeltaZ > 0) {
191*4882a593Smuzhiyun         button = iButtonUp;
192*4882a593Smuzhiyun     }
193*4882a593Smuzhiyun     else {
194*4882a593Smuzhiyun         button = iButtonDown;
195*4882a593Smuzhiyun     }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun     /*
198*4882a593Smuzhiyun      * Flip iDeltaZ to positive, if negative,
199*4882a593Smuzhiyun      * because always need to generate a *positive* number of
200*4882a593Smuzhiyun      * button clicks for the Z axis.
201*4882a593Smuzhiyun      */
202*4882a593Smuzhiyun     if (iDeltaZ < 0) {
203*4882a593Smuzhiyun         iDeltaZ *= -1;
204*4882a593Smuzhiyun     }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun     /* Generate X input messages for each wheel delta we have seen */
207*4882a593Smuzhiyun     while (iDeltaZ--) {
208*4882a593Smuzhiyun         /* Push the wheel button */
209*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonPress, button);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun         /* Release the wheel button */
212*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonRelease, button);
213*4882a593Smuzhiyun     }
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun     return 0;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun /*
219*4882a593Smuzhiyun  * Enqueue a mouse button event
220*4882a593Smuzhiyun  */
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun void
winMouseButtonsSendEvent(int iEventType,int iButton)223*4882a593Smuzhiyun winMouseButtonsSendEvent(int iEventType, int iButton)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun     ValuatorMask mask;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun     if (g_winMouseButtonMap)
228*4882a593Smuzhiyun         iButton = g_winMouseButtonMap[iButton];
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun     valuator_mask_zero(&mask);
231*4882a593Smuzhiyun     QueuePointerEvents(g_pwinPointer, iEventType, iButton,
232*4882a593Smuzhiyun                        POINTER_RELATIVE, &mask);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun #if CYGDEBUG
235*4882a593Smuzhiyun     ErrorF("winMouseButtonsSendEvent: iEventType: %d, iButton: %d\n",
236*4882a593Smuzhiyun            iEventType, iButton);
237*4882a593Smuzhiyun #endif
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun /*
241*4882a593Smuzhiyun  * Decide what to do with a Windows mouse message
242*4882a593Smuzhiyun  */
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun int
winMouseButtonsHandle(ScreenPtr pScreen,int iEventType,int iButton,WPARAM wParam)245*4882a593Smuzhiyun winMouseButtonsHandle(ScreenPtr pScreen,
246*4882a593Smuzhiyun                       int iEventType, int iButton, WPARAM wParam)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun     winScreenPriv(pScreen);
249*4882a593Smuzhiyun     winScreenInfo *pScreenInfo = pScreenPriv->pScreenInfo;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun     /* Send button events right away if emulate 3 buttons is off */
252*4882a593Smuzhiyun     if (pScreenInfo->iE3BTimeout == WIN_E3B_OFF) {
253*4882a593Smuzhiyun         /* Emulate 3 buttons is off, send the button event */
254*4882a593Smuzhiyun         winMouseButtonsSendEvent(iEventType, iButton);
255*4882a593Smuzhiyun         return 0;
256*4882a593Smuzhiyun     }
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun     /* Emulate 3 buttons is on, let the fun begin */
259*4882a593Smuzhiyun     if (iEventType == ButtonPress
260*4882a593Smuzhiyun         && pScreenPriv->iE3BCachedPress == 0
261*4882a593Smuzhiyun         && !pScreenPriv->fE3BFakeButton2Sent) {
262*4882a593Smuzhiyun         /*
263*4882a593Smuzhiyun          * Button was pressed, no press is cached,
264*4882a593Smuzhiyun          * and there is no fake button 2 release pending.
265*4882a593Smuzhiyun          */
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun         /* Store button press type */
268*4882a593Smuzhiyun         pScreenPriv->iE3BCachedPress = iButton;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun         /*
271*4882a593Smuzhiyun          * Set a timer to send this button press if the other button
272*4882a593Smuzhiyun          * is not pressed within the timeout time.
273*4882a593Smuzhiyun          */
274*4882a593Smuzhiyun         SetTimer(pScreenPriv->hwndScreen,
275*4882a593Smuzhiyun                  WIN_E3B_TIMER_ID, pScreenInfo->iE3BTimeout, NULL);
276*4882a593Smuzhiyun     }
277*4882a593Smuzhiyun     else if (iEventType == ButtonPress
278*4882a593Smuzhiyun              && pScreenPriv->iE3BCachedPress != 0
279*4882a593Smuzhiyun              && pScreenPriv->iE3BCachedPress != iButton
280*4882a593Smuzhiyun              && !pScreenPriv->fE3BFakeButton2Sent) {
281*4882a593Smuzhiyun         /*
282*4882a593Smuzhiyun          * Button press is cached, other button was pressed,
283*4882a593Smuzhiyun          * and there is no fake button 2 release pending.
284*4882a593Smuzhiyun          */
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun         /* Mouse button was cached and other button was pressed */
287*4882a593Smuzhiyun         KillTimer(pScreenPriv->hwndScreen, WIN_E3B_TIMER_ID);
288*4882a593Smuzhiyun         pScreenPriv->iE3BCachedPress = 0;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun         /* Send fake middle button */
291*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonPress, Button2);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun         /* Indicate that a fake middle button event was sent */
294*4882a593Smuzhiyun         pScreenPriv->fE3BFakeButton2Sent = TRUE;
295*4882a593Smuzhiyun     }
296*4882a593Smuzhiyun     else if (iEventType == ButtonRelease
297*4882a593Smuzhiyun              && pScreenPriv->iE3BCachedPress == iButton) {
298*4882a593Smuzhiyun         /*
299*4882a593Smuzhiyun          * Cached button was released before timer ran out,
300*4882a593Smuzhiyun          * and before the other mouse button was pressed.
301*4882a593Smuzhiyun          */
302*4882a593Smuzhiyun         KillTimer(pScreenPriv->hwndScreen, WIN_E3B_TIMER_ID);
303*4882a593Smuzhiyun         pScreenPriv->iE3BCachedPress = 0;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun         /* Send cached press, then send release */
306*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonPress, iButton);
307*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonRelease, iButton);
308*4882a593Smuzhiyun     }
309*4882a593Smuzhiyun     else if (iEventType == ButtonRelease
310*4882a593Smuzhiyun              && pScreenPriv->fE3BFakeButton2Sent && !(wParam & MK_LBUTTON)
311*4882a593Smuzhiyun              && !(wParam & MK_RBUTTON)) {
312*4882a593Smuzhiyun         /*
313*4882a593Smuzhiyun          * Fake button 2 was sent and both mouse buttons have now been released
314*4882a593Smuzhiyun          */
315*4882a593Smuzhiyun         pScreenPriv->fE3BFakeButton2Sent = FALSE;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun         /* Send middle mouse button release */
318*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonRelease, Button2);
319*4882a593Smuzhiyun     }
320*4882a593Smuzhiyun     else if (iEventType == ButtonRelease
321*4882a593Smuzhiyun              && pScreenPriv->iE3BCachedPress == 0
322*4882a593Smuzhiyun              && !pScreenPriv->fE3BFakeButton2Sent) {
323*4882a593Smuzhiyun         /*
324*4882a593Smuzhiyun          * Button was release, no button is cached,
325*4882a593Smuzhiyun          * and there is no fake button 2 release is pending.
326*4882a593Smuzhiyun          */
327*4882a593Smuzhiyun         winMouseButtonsSendEvent(ButtonRelease, iButton);
328*4882a593Smuzhiyun     }
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun     return 0;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun /**
334*4882a593Smuzhiyun  * Enqueue a motion event.
335*4882a593Smuzhiyun  *
336*4882a593Smuzhiyun  */
337*4882a593Smuzhiyun void
winEnqueueMotion(int x,int y)338*4882a593Smuzhiyun winEnqueueMotion(int x, int y)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun     int valuators[2];
341*4882a593Smuzhiyun     ValuatorMask mask;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun     valuators[0] = x;
344*4882a593Smuzhiyun     valuators[1] = y;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun     valuator_mask_set_range(&mask, 0, 2, valuators);
347*4882a593Smuzhiyun     QueuePointerEvents(g_pwinPointer, MotionNotify, 0,
348*4882a593Smuzhiyun                        POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun }
351