Windows与Mac双剑合璧之剪切板增强Synergy-clipboard

背景介绍

JavaScript 诞生于 1995 年。它当时的目的是为了验证表单输入的验证,当时在服务器端的Rhino不流行,主要运用客户端。经过许多年的发展,JavaScript 从一个简单的输入验证成为一门强大的编程语言。
2005 年 Jesse James Garrett 发表的一篇文章里介绍了一种技术Ajax。这种技术能够让请求服务器额外的数据无须刷新,会带来更好的用户体验,一时间,席卷全球。
2009 年 Ryan Dahl使用基于Google V8引擎编写了Node.js,使用libuv封装了Windows的IOCP及Unix的libev实现跨平台。基于Node.js产生了许多新的技术,例如基于Node.js的TypeScript语言编写的AngularJS,基于Node.js的Webpack打包工具用来构建Vue.js,基于Node.js的构建跨平台的桌面应用Electron。
随着Node.js的出现,前端越来越火爆,随之产生的技术栈已经远远超越后端,下面简要列举一些业界常用的。

语言核心

  • ECMAScript 3
  • ECMAScript 5
  • ECMAScript 6

规范

  • CommonJS
  • ES6
  • AMD
  • CMD

扩展语言

  • TypeScript
  • CoffeeScript
  • JSX

Web框架

  • Express
  • Koa
  • Egg
  • ThinkJS
  • Meteor
  • SailsJS

前端三大框架

  • Vue.js
  • React
  • Aungluar

函数式编程

  • underscore
  • loadash

异步编程

  • Promise
  • Generator + co
  • Async/Await

构建工具

  • NPM Scripts
  • webpack
  • gulp
  • grunt
  • fis
  • RequireJS
  • rollup
  • Parcel

命令行工具

  • browserify
  • pkg
  • Yoman
  • Bower
  • Browser-run
  • LiveServer
  • Babel
  • Yargs
  • hexo
  • gitbook

实时通信

  • Socket.io
  • SockJS

测试框架

  • ava.js
  • Mocha
  • nodeunit

CSS框架

  • BootStrap
  • Element
  • pintuer
  • Bulma

CSS预处理

  • Sass
  • Less
  • Stylus
  • PostCSS

Html预处理

  • Pug
  • EJS
  • Handlebars

代码优化

  • CSS Lint
  • JSHint
  • JSLint
  • UglifyJS
  • Prepack

服务器布署

  • pm2
  • forever
  • supervisor

数据库

  • MongoDB
  • LevelDb
  • CouchDB

文本处理

  • Underscore.string
  • incov-lite

其他

  • Moment.js
  • RunKit
  • JSDoc

对于这么多技术,大部分类似的技术只需挑选一两个合适的掌握就好,比如CSS预处理语言我选的是Sass,基于Sass的框架有Compass,Bourbon和Susy。我选择Compass。有些需要掌握几个相互串联的技术,如Web框架则需掌握Express+Koa+Egg或Express+Koa+ThinkJS,Koa基于Express,Egg和ThinkJS都是基于Koa。
作为PHPer的我,近两年也将精力转向了前端。做前端开发,发现在Windows上实在是不太友好,主要是Windows上的Bash都不太好。Git Bash,Cmder,Hyper都不能完美替代Linux上的Bash,不能很好地运行我最爱的Vim编辑器和Emacs操作系统。

Vim上居然给我显示这个Microsoft Windows [版本 6.1.7601]。Ctrl+C时显示终止批处理操作吗(Y/N)?,真是麻烦。也试过将Windows上的磁盘同步到Vagrant CentOS上,在Vagrant上安装了Linux的npm包,有时需要在Windows上调试,感觉还是不太好用。于是想到了前端工程师必备的Mac,前端时间买了MacBook,于是有了两台电脑,想找到一个好的方法可以同时使用Windows和Mac,在Windows上使用TightVNC连接Mac和在Mac上使用Microsoft Remote Desktop连接Windows,网速慢的时候比较卡,而且占用屏幕空间。直到最近我发现了一个神器Synergy,这个软件可以在两台电脑上用一套鼠标和键盘,完全不用占用网络和屏幕空间,直接可以把一台电脑上的鼠标移到另一台电脑上,使用同一个键盘打字,使用同一个剪切板粘贴文字,简直Perfect。有了这个软件,从此工作效率极大提升。
以下是我Mac上的Vim和Spacemacs,简直太好用了。

剪切板增强(Synergy-clipbaord)

Synergy是个开源软件,最后一个免费版本是1.5.0,后面的版本收费但仍开源。我的是在脚本之家上下载的1.5.0版本。经测试两个版本必须一致才能共用剪切板,剪切板只能剪切文字,至于网上说的可以直接把Windows上的文件拖到Mac上发现用不了;不同版本只能共用键鼠,无法共用剪切板。
用了一段时间后,经常在Windows上浏览网页,在Mac上写代码,有时代码中包含太长的字符串字面量,就会直接在Windows上复制在Mac上粘贴,发现在网页上复制的文字在Mac上无法粘贴,于是粘贴到Notepad++在复制粘贴就可以了,不过这显然太麻烦了。于是就自己动手写了个小工具,见 https://github.com/NetworkRanger/synergy-clipboard 。运行这个工具就可以在Windows浏览器上复制的文字直接粘贴到Mac上。以下是源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <windows.h>
#include "resource.h"

#define WM_TRAY (WM_USER + 100)
#define WM_TASKBAR_CREATED RegisterWindowMessage(TEXT("TaskbarCreated"))

#define APP_NAME TEXT("托盘程序")
#define APP_TIP TEXT("Win32 API 实现系统托盘程序")

NOTIFYICONDATA nid; //托盘属性
HMENU hMenu; //托盘菜单

//实例化托盘
void InitTray(HINSTANCE hInstance, HWND hWnd)
{
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = IDI_TRAY;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
nid.uCallbackMessage = WM_TRAY;
nid.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TRAY));
lstrcpy(nid.szTip, APP_NAME);
hMenu = CreatePopupMenu();//生成托盘菜单
AppendMenu(hMenu, MF_STRING, ID_EXIT, TEXT("退出"));
Shell_NotifyIcon(NIM_ADD, &nid);
}

//窗口过程函数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
//定义窗口类结构体变量
WNDCLASS wc;

//窗口类名称
static TCHAR *szAppName = TEXT("IPHPJS");

//窗口句柄
HWND hwnd = NULL;

//消息结构
MSG msg;

/**
下面的代码填充窗口类信息,如图标,鼠标样式,背景,过程函数等
*/
wc.style = CS_HREDRAW | CS_VREDRAW; //窗口样式
wc.lpfnWndProc = WndProc; //过程函数
wc.cbClsExtra = 0; //扩展字段
wc.cbWndExtra = 0; //扩展字段
wc.hInstance = hInstance; //当前实例句柄
wc.hIcon = ::LoadIcon(hInstance, IDI_APPLICATION); //设置程序图标
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); //设置鼠标

//用白色画刷填充背景
wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);

//菜单
wc.lpszMenuName = NULL;

//类名
wc.lpszClassName = szAppName;


//像操作系统注册窗口类
if (!::RegisterClass(&wc)) {
::MessageBox(NULL, TEXT("程序只能在windowsNT下运行"),
szAppName, MB_ICONERROR);
return 0;
}

//创建窗口
hwnd = ::CreateWindow(szAppName, //要注册的窗口类名
TEXT("Synergy-clipboard"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口样式
CW_USEDEFAULT, //窗口距离屏幕左上角的横坐标
CW_USEDEFAULT, //窗口距离屏幕左上角的纵坐标
400, //窗口宽度
300, //窗口高度
NULL, //父窗口句柄
NULL, //菜单句柄
hInstance, //当前实例句柄
NULL); //指向一个值的指针,该值传递给窗口 WM_CREATE消息。一般为NULL

//显示窗口
//::ShowWindow(hwnd, iCmdShow);

//更新窗口
::UpdateWindow(hwnd);
InitTray(hInstance, hwnd);

//消息循环,一直停在这里,退出消息循环就表示程序结束了。
while (::GetMessage(&msg, NULL, 0, 0)) {
//翻译消息
::TranslateMessage(&msg);

//分发消息
::DispatchMessage(&msg);
}

return msg.wParam;
}


/**
消息处理函数
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
static HWND hwndNextViewer;
switch (message) {
case WM_DRAWCLIPBOARD:{
if (OpenClipboard(NULL)){
if (IsClipboardFormatAvailable(CF_TEXT)){
HANDLE hClipBoard = GetClipboardData(CF_TEXT);
char *szBuf = (char *)GlobalLock(hClipBoard);
GlobalUnlock(hClipBoard);
EmptyClipboard(); // 清空
HGLOBAL clipBuffer;
int nLen = strlen(szBuf) + 1;
clipBuffer = GlobalAlloc(GMEM_DDESHARE, nLen);
char *buffer = (char *)GlobalLock(clipBuffer);
memcpy_s(buffer, nLen, szBuf, nLen);
GlobalUnlock(clipBuffer);
SetClipboardData(CF_TEXT, clipBuffer);
}
CloseClipboard();
}
break;
}
case WM_TRAY:
switch (lParam)
{
case WM_RBUTTONDOWN:
//获取鼠标坐标
POINT pt; GetCursorPos(&pt);
//解决在菜单外单击左键菜单不消失的问题
SetForegroundWindow(hwnd);
//显示并获取选中的菜单
int cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, NULL, hwnd, NULL);
if (cmd == ID_EXIT)
PostMessage(hwnd, WM_DESTROY, NULL, NULL);
break;
}
return 0;
case WM_CREATE:{
hwndNextViewer = ::SetClipboardViewer(hwnd);
break;
}
case WM_DESTROY:
//窗口销毁时删除托盘
Shell_NotifyIcon(NIM_DELETE, &nid);
//发送结束请求,里面的参数为退出码
::PostQuitMessage(0);
break;
}

//调用默认的过程函数
return ::DefWindowProc(hwnd, message, wParam, lParam);
}

呵呵,以后就可以方便地同时使用Windows与Mac两台电脑了~~~