Browse Source

📊 Separate QR code scanning from MenuViewController

tags/4.3.1.0
database64128 4 years ago
parent
commit
5f760a5e57
No known key found for this signature in database GPG Key ID: 1CA27546BEDB8B01
5 changed files with 62 additions and 371 deletions
  1. +1
    -1
      shadowsocks-csharp/Data/i18n.csv
  2. +44
    -4
      shadowsocks-csharp/Util/Util.cs
  3. +17
    -82
      shadowsocks-csharp/View/MenuViewController.cs
  4. +0
    -281
      shadowsocks-csharp/View/QRCodeSplashForm.cs
  5. +0
    -3
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 1
- 1
shadowsocks-csharp/Data/i18n.csv View File

@@ -128,7 +128,7 @@ No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution.
Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification.
"If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire."
Failed to decode QRCode,Не удалось распознать QRCode,无法解析二维码,QR 碼解碼失敗,QR コードの読み取りに失敗しました。,QR코드를 해석하는데에 실패했습니다.,Impossible de décoder le QRCode
Invalid QR Code content: {0},,无效二维码内容: {0},無效二維碼內容: {0},,,
Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,,
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,,


+ 44
- 4
shadowsocks-csharp/Util/Util.cs View File

@@ -8,6 +8,10 @@ using System.Windows.Forms;
using Microsoft.Win32;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using System.Drawing;
using ZXing;
using ZXing.QrCode;
using ZXing.Common;
namespace Shadowsocks.Util
{
@@ -227,11 +231,47 @@ namespace Shadowsocks.Util
return Environment.OSVersion.Version.Major > 5;
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process,
UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
public static string ScanQRCodeFromScreen()
{
foreach (Screen screen in Screen.AllScreens)
{
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
screen.Bounds.Height))
{
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(screen.Bounds.X,
screen.Bounds.Y,
0, 0,
fullImage.Size,
CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
var source = new BitmapLuminanceSource(target);
var bitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(bitmap);
if (result != null)
return result.Text;
}
}
}
return null;
}
// See: https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
public static bool IsSupportedRuntimeVersion()


+ 17
- 82
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -714,12 +714,7 @@ namespace Shadowsocks.View
ShowConfigForm();
}
void splash_FormClosed(object sender, FormClosedEventArgs e)
{
ShowConfigForm();
}
void openURLFromQRCode(object sender, FormClosedEventArgs e)
void openURLFromQRCode()
{
Process.Start(_urlToOpen);
}
@@ -751,86 +746,26 @@ namespace Shadowsocks.View
private void ScanQRCodeItem_Click(object sender, EventArgs e)
{
foreach (Screen screen in Screen.AllScreens)
var result = Utils.ScanQRCodeFromScreen();
if (result != null)
{
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
screen.Bounds.Height))
if (result.ToLowerInvariant().StartsWith("http://") || result.ToLowerInvariant().StartsWith("https://"))
{
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(screen.Bounds.X,
screen.Bounds.Y,
0, 0,
fullImage.Size,
CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
var source = new BitmapLuminanceSource(target);
var bitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(bitmap);
if (result != null)
{
var success = controller.AddServerBySSURL(result.Text);
QRCodeSplashForm splash = new QRCodeSplashForm();
if (success)
{
splash.FormClosed += splash_FormClosed;
}
else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://"))
{
_urlToOpen = result.Text;
splash.FormClosed += openURLFromQRCode;
}
else
{
MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
return;
}
double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
foreach (ResultPoint point in result.ResultPoints)
{
minX = Math.Min(minX, point.X);
minY = Math.Min(minY, point.Y);
maxX = Math.Max(maxX, point.X);
maxY = Math.Max(maxY, point.Y);
}
minX /= imageScale;
minY /= imageScale;
maxX /= imageScale;
maxY /= imageScale;
// make it 20% larger
double margin = (maxX - minX) * 0.20f;
minX += -margin + marginLeft;
maxX += margin + marginLeft;
minY += -margin + marginTop;
maxY += margin + marginTop;
splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
// we need a panel because a window has a minimal size
// TODO: test on high DPI
splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY);
splash.Size = new Size(fullImage.Width, fullImage.Height);
splash.Show();
return;
}
}
_urlToOpen = result;
openURLFromQRCode();
}
else if (controller.AddServerBySSURL(result))
{
ShowConfigForm();
}
else
{
MessageBox.Show(I18N.GetString("Invalid QR Code content: {0}", result));
}
return;
}
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
else
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
}
private void ImportURLItem_Click(object sender, EventArgs e)


+ 0
- 281
shadowsocks-csharp/View/QRCodeSplashForm.cs View File

@@ -1,281 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Shadowsocks.View
{
public class QRCodeSplashForm : Form
{
public Rectangle TargetRect;
public QRCodeSplashForm()
{
FormBorderStyle = FormBorderStyle.None;
this.Load += QRCodeSplashForm_Load;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(1, 1);
this.ControlBox = false;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "QRCodeSplashForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.TopMost = true;
}
private Timer timer;
private int flashStep;
private static double FPS = 1.0 / 15 * 1000; // System.Windows.Forms.Timer resolution is 15ms
private static double ANIMATION_TIME = 0.5;
private static int ANIMATION_STEPS = (int)(ANIMATION_TIME * FPS);
Stopwatch sw;
int x;
int y;
int w;
int h;
Bitmap bitmap;
Graphics g;
Pen pen;
SolidBrush brush;
private void QRCodeSplashForm_Load(object sender, EventArgs e)
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
flashStep = 0;
x = 0;
y = 0;
w = Width;
h = Height;
sw = Stopwatch.StartNew();
timer = new Timer();
timer.Interval = (int)(ANIMATION_TIME * 1000 / ANIMATION_STEPS);
timer.Tick += timer_Tick;
timer.Start();
bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
g = Graphics.FromImage(bitmap);
pen = new Pen(Color.Red, 3);
brush = new SolidBrush(Color.FromArgb(30, Color.Red));
}
void timer_Tick(object sender, EventArgs e)
{
double percent = (double)sw.ElapsedMilliseconds / 1000.0 / (double)ANIMATION_TIME;
if (percent < 1)
{
// ease out
percent = 1 - Math.Pow((1 - percent), 4);
x = (int)(TargetRect.X * percent);
y = (int)(TargetRect.Y * percent);
w = (int)(TargetRect.Width * percent + this.Size.Width * (1 - percent));
h = (int)(TargetRect.Height * percent + this.Size.Height * (1 - percent));
//codeRectView.Location = new Point(x, y);
//codeRectView.Size = new Size(w, h);
pen.Color = Color.FromArgb((int)(255 * percent), Color.Red);
brush.Color = Color.FromArgb((int)(30 * percent), Color.Red);
g.Clear(Color.Transparent);
g.FillRectangle(brush, x, y, w, h);
g.DrawRectangle(pen, x, y, w, h);
SetBitmap(bitmap);
}
else
{
if (flashStep == 0)
{
timer.Interval = 100;
g.Clear(Color.Transparent);
SetBitmap(bitmap);
}
else if (flashStep == 1)
{
timer.Interval = 50;
g.FillRectangle(brush, x, y, w, h);
g.DrawRectangle(pen, x, y, w, h);
SetBitmap(bitmap);
}
else if (flashStep == 2)
{
g.Clear(Color.Transparent);
SetBitmap(bitmap);
}
else if (flashStep == 3)
{
g.FillRectangle(brush, x, y, w, h);
g.DrawRectangle(pen, x, y, w, h);
SetBitmap(bitmap);
}
else if (flashStep == 4)
{
g.Clear(Color.Transparent);
SetBitmap(bitmap);
}
else if (flashStep == 5)
{
g.FillRectangle(brush, x, y, w, h);
g.DrawRectangle(pen, x, y, w, h);
SetBitmap(bitmap);
}
else
{
sw.Stop();
timer.Stop();
pen.Dispose();
brush.Dispose();
bitmap.Dispose();
this.Close();
}
flashStep++;
}
}
// PerPixelAlphaForm.cs
// http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C
// Rui Lopes
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style
return cp;
}
}
public void SetBitmap(Bitmap bitmap)
{
SetBitmap(bitmap, 255);
}
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para>
public void SetBitmap(Bitmap bitmap, byte opacity)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
// The idea of this is very simple,
// 1. Create a compatible DC with screen;
// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
// 3. Call the UpdateLayeredWindow.
IntPtr screenDc = Win32.GetDC(IntPtr.Zero);
IntPtr memDc = Win32.CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr oldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap
oldBitmap = Win32.SelectObject(memDc, hBitmap);
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height);
Win32.Point pointSource = new Win32.Point(0, 0);
Win32.Point topPos = new Win32.Point(Left, Top);
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION();
blend.BlendOp = Win32.AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = opacity;
blend.AlphaFormat = Win32.AC_SRC_ALPHA;
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
Win32.SelectObject(memDc, oldBitmap);
//Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak.
Win32.DeleteObject(hBitmap);
}
Win32.DeleteDC(memDc);
}
}
}
// class that exposes needed win32 gdi functions.
class Win32
{
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern int DeleteObject(IntPtr hObject);
}
}

+ 0
- 3
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -332,9 +332,6 @@
<DependentUpon>LogForm.cs</DependentUpon>
</Compile>
<Compile Include="View\MenuViewController.cs" />
<Compile Include="View\QRCodeSplashForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Views\ServerSharingView.xaml.cs">
<DependentUpon>ServerSharingView.xaml</DependentUpon>
</Compile>


Loading…
Cancel
Save