1 /*
2  * Copyright (c) 2002-2016, the original author or authors.
3  *
4  * This software is distributable under the BSD license. See the terms of the
5  * BSD license in the documentation provided with this software.
6  *
7  * https://opensource.org/licenses/BSD-3-Clause
8  */
9 package jdk.internal.org.jline.terminal.impl;
10 
11 import jdk.internal.org.jline.terminal.MouseEvent;
12 import jdk.internal.org.jline.terminal.Terminal;
13 import jdk.internal.org.jline.utils.InfoCmp;
14 import jdk.internal.org.jline.utils.InputStreamReader;
15 
16 import java.io.EOFException;
17 import java.io.IOError;
18 import java.io.IOException;
19 import java.nio.charset.StandardCharsets;
20 import java.util.EnumSet;
21 import java.util.function.IntSupplier;
22 
23 public class MouseSupport {
24 
hasMouseSupport(Terminal terminal)25     public static boolean hasMouseSupport(Terminal terminal) {
26         return terminal.getStringCapability(InfoCmp.Capability.key_mouse) != null;
27     }
28 
trackMouse(Terminal terminal, Terminal.MouseTracking tracking)29     public static boolean trackMouse(Terminal terminal, Terminal.MouseTracking tracking) {
30         if (hasMouseSupport(terminal)) {
31             switch (tracking) {
32                 case Off:
33                     terminal.writer().write("\033[?1000l");
34                     break;
35                 case Normal:
36                     terminal.writer().write("\033[?1005h\033[?1000h");
37                     break;
38                 case Button:
39                     terminal.writer().write("\033[?1005h\033[?1002h");
40                     break;
41                 case Any:
42                     terminal.writer().write("\033[?1005h\033[?1003h");
43                     break;
44             }
45             terminal.flush();
46             return true;
47         } else {
48             return false;
49         }
50     }
51 
readMouse(Terminal terminal, MouseEvent last)52     public static MouseEvent readMouse(Terminal terminal, MouseEvent last) {
53         return readMouse(() -> readExt(terminal), last);
54     }
55 
readMouse(IntSupplier reader, MouseEvent last)56     public static MouseEvent readMouse(IntSupplier reader, MouseEvent last) {
57         int cb = reader.getAsInt() - ' ';
58         int cx = reader.getAsInt() - ' ' - 1;
59         int cy = reader.getAsInt() - ' ' - 1;
60         MouseEvent.Type type;
61         MouseEvent.Button button;
62         EnumSet<MouseEvent.Modifier> modifiers = EnumSet.noneOf(MouseEvent.Modifier.class);
63         if ((cb & 4) == 4) {
64             modifiers.add(MouseEvent.Modifier.Shift);
65         }
66         if ((cb & 8) == 8) {
67             modifiers.add(MouseEvent.Modifier.Alt);
68         }
69         if ((cb & 16) == 16) {
70             modifiers.add(MouseEvent.Modifier.Control);
71         }
72         if ((cb & 64) == 64) {
73             type = MouseEvent.Type.Wheel;
74             button = (cb & 1) == 1 ? MouseEvent.Button.WheelDown : MouseEvent.Button.WheelUp;
75         } else {
76             int b = (cb & 3);
77             switch (b) {
78                 case 0:
79                     button = MouseEvent.Button.Button1;
80                     if (last.getButton() == button
81                             && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) {
82                         type = MouseEvent.Type.Dragged;
83                     } else {
84                         type = MouseEvent.Type.Pressed;
85                     }
86                     break;
87                 case 1:
88                     button = MouseEvent.Button.Button2;
89                     if (last.getButton() == button
90                             && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) {
91                         type = MouseEvent.Type.Dragged;
92                     } else {
93                         type = MouseEvent.Type.Pressed;
94                     }
95                     break;
96                 case 2:
97                     button = MouseEvent.Button.Button3;
98                     if (last.getButton() == button
99                             && (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged)) {
100                         type = MouseEvent.Type.Dragged;
101                     } else {
102                         type = MouseEvent.Type.Pressed;
103                     }
104                     break;
105                 default:
106                     if (last.getType() == MouseEvent.Type.Pressed || last.getType() == MouseEvent.Type.Dragged) {
107                         button = last.getButton();
108                         type = MouseEvent.Type.Released;
109                     } else {
110                         button = MouseEvent.Button.NoButton;
111                         type = MouseEvent.Type.Moved;
112                     }
113                     break;
114             }
115         }
116         return new MouseEvent(type, button, modifiers, cx, cy);
117     }
118 
readExt(Terminal terminal)119     private static int readExt(Terminal terminal) {
120         try {
121             // The coordinates are encoded in UTF-8, so if that's not the input encoding,
122             // we need to get around
123             int c;
124             if (terminal.encoding() != StandardCharsets.UTF_8) {
125                 c = new InputStreamReader(terminal.input(), StandardCharsets.UTF_8).read();
126             } else {
127                 c = terminal.reader().read();
128             }
129             if (c < 0) {
130                 throw new EOFException();
131             }
132             return c;
133         } catch (IOException e) {
134             throw new IOError(e);
135         }
136     }
137 
138 }
139