mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
68dca6e3d9 | |||
017f883e0b | |||
77b5c95f75 | |||
9d052c8339 | |||
d67623a657 | |||
c740b3dd46 | |||
2ef5f7f96f | |||
404568d795 | |||
b5a6337a0c | |||
82170c3fbd | |||
e6d6275fcc | |||
97c865a127 | |||
1ff21f0ee0 | |||
2a3dca4467 | |||
d4ecfcceec | |||
ec5d503e4d | |||
346391ca2d | |||
9074cdf340 | |||
2fcf3604a8 | |||
34e5185fa1 | |||
e09e0e69ca | |||
963c98e588 | |||
92acb823a4 | |||
b967b1288f | |||
1db271ce90 | |||
58c64025e3 | |||
643a7a87aa |
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
@@ -137,7 +138,7 @@ namespace TweetDuck.Configuration{
|
|||||||
public bool RestoreLockingProcess(int failTimeout){
|
public bool RestoreLockingProcess(int failTimeout){
|
||||||
if (lockingProcess != null){
|
if (lockingProcess != null){
|
||||||
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, Program.WindowRestoreMessage, new UIntPtr((uint)lockingProcess.Id), IntPtr.Zero);
|
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
||||||
|
|
||||||
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
||||||
return true;
|
return true;
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
static class ControlExtensions{
|
static class ControlExtensions{
|
||||||
@@ -70,7 +71,7 @@ namespace TweetDuck.Core.Controls{
|
|||||||
public static void SetElevated(this Button button){
|
public static void SetElevated(this Button button){
|
||||||
button.Text = " "+button.Text;
|
button.Text = " "+button.Text;
|
||||||
button.FlatStyle = FlatStyle.System;
|
button.FlatStyle = FlatStyle.System;
|
||||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, new UIntPtr(0), new IntPtr(1));
|
Comms.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnableMultilineShortcuts(this TextBox textBox){
|
public static void EnableMultilineShortcuts(this TextBox textBox){
|
||||||
|
@@ -87,6 +87,7 @@ namespace TweetDuck.Core{
|
|||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
MenuHandler = new ContextMenuBrowser(this),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
};
|
};
|
||||||
@@ -389,16 +390,6 @@ namespace TweetDuck.Core{
|
|||||||
BrowserProcesses.Link(m.LParam.ToInt32(), processId);
|
BrowserProcesses.Link(m.LParam.ToInt32(), processId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (m.Msg == Program.VideoPlayerMessage){
|
|
||||||
int volume = m.WParam.ToInt32();
|
|
||||||
|
|
||||||
if (Handle == m.LParam && volume != Config.VideoPlayerVolume){
|
|
||||||
Config.VideoPlayerVolume = volume;
|
|
||||||
Config.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,6 +528,15 @@ namespace TweetDuck.Core{
|
|||||||
videoPlayer.Launch(url);
|
videoPlayer.Launch(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ToggleVideoPause(){
|
||||||
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
|
videoPlayer.TogglePause();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public void HideVideoOverlay(){
|
public void HideVideoOverlay(){
|
||||||
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||||
}
|
}
|
||||||
|
24
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
24
Core/Handling/KeyboardHandlerBrowser.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
|
||||||
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
|
public KeyboardHandlerBrowser(FormBrowser form){
|
||||||
|
this.form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
|
if (type == KeyType.RawKeyDown && (Keys)windowsKeyCode == Keys.Space){
|
||||||
|
return form.ToggleVideoPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Management{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
sealed class VideoPlayer : IDisposable{
|
sealed class VideoPlayer : IDisposable{
|
||||||
@@ -23,23 +24,32 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
public event EventHandler ProcessExited;
|
public event EventHandler ProcessExited;
|
||||||
|
|
||||||
private readonly Form owner;
|
private readonly Form owner;
|
||||||
private Process currentProcess;
|
|
||||||
private string lastUrl;
|
private string lastUrl;
|
||||||
|
|
||||||
|
private Process currentProcess;
|
||||||
|
private DuplexPipe.Server currentPipe;
|
||||||
|
private bool isClosing;
|
||||||
|
|
||||||
public VideoPlayer(Form owner){
|
public VideoPlayer(Form owner){
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.owner.FormClosing += owner_FormClosing;
|
this.owner.FormClosing += owner_FormClosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Launch(string url){
|
public void Launch(string url){
|
||||||
Close();
|
if (Running){
|
||||||
|
Destroy();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
|
||||||
lastUrl = url;
|
lastUrl = url;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
currentPipe = DuplexPipe.CreateServer();
|
||||||
|
currentPipe.DataIn += currentPipe_DataIn;
|
||||||
|
|
||||||
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
||||||
FileName = PlayerExe,
|
FileName = PlayerExe,
|
||||||
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\"",
|
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
})) != null){
|
})) != null){
|
||||||
@@ -51,15 +61,65 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
|
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentPipe.DisposeToken();
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
|
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TogglePause(){
|
||||||
|
currentPipe?.Write("pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
||||||
|
owner.InvokeSafe(() => {
|
||||||
|
switch(e.Key){
|
||||||
|
case "vol":
|
||||||
|
if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){
|
||||||
|
Program.UserConfig.VideoPlayerVolume = volume;
|
||||||
|
Program.UserConfig.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "rip":
|
||||||
|
currentPipe.Dispose();
|
||||||
|
currentPipe = null;
|
||||||
|
|
||||||
|
currentProcess.Dispose();
|
||||||
|
currentProcess = null;
|
||||||
|
|
||||||
|
isClosing = false;
|
||||||
|
TriggerProcessExitEventUnsafe();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void Close(){
|
public void Close(){
|
||||||
if (currentProcess != null){
|
if (currentProcess != null){
|
||||||
currentProcess.Exited -= process_Exited;
|
if (isClosing){
|
||||||
|
Destroy();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
isClosing = true;
|
||||||
|
currentProcess.Exited -= process_Exited;
|
||||||
|
currentPipe.Write("die");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
ProcessExited = null;
|
||||||
|
|
||||||
|
isClosing = true;
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Destroy(){
|
||||||
|
if (currentProcess != null){
|
||||||
try{
|
try{
|
||||||
currentProcess.Kill();
|
currentProcess.Kill();
|
||||||
}catch{
|
}catch{
|
||||||
@@ -69,13 +129,11 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
currentProcess.Dispose();
|
currentProcess.Dispose();
|
||||||
currentProcess = null;
|
currentProcess = null;
|
||||||
|
|
||||||
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
currentPipe.Dispose();
|
||||||
}
|
currentPipe = null;
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose(){
|
TriggerProcessExitEventUnsafe();
|
||||||
ProcessExited = null;
|
}
|
||||||
Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void owner_FormClosing(object sender, FormClosingEventArgs e){
|
private void owner_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
@@ -104,6 +162,9 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
currentProcess.Dispose();
|
currentProcess.Dispose();
|
||||||
currentProcess = null;
|
currentProcess = null;
|
||||||
|
|
||||||
|
currentPipe.Dispose();
|
||||||
|
currentPipe = null;
|
||||||
|
|
||||||
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
|
||||||
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
||||||
|
|
||||||
public const int HWND_TOPMOST = -1;
|
public const int HWND_TOPMOST = -1;
|
||||||
@@ -66,15 +65,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern uint RegisterWindowMessage(string messageName);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
|
||||||
|
17
Program.cs
17
Program.cs
@@ -1,4 +1,4 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -14,14 +14,15 @@ using TweetDuck.Core.Other.Settings.Export;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
static class Program{
|
static class Program{
|
||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.8.5.1";
|
public const string VersionTag = "1.8.6";
|
||||||
public const string VersionFull = "1.8.5.1";
|
public const string VersionFull = "1.8.6";
|
||||||
|
|
||||||
public static readonly Version Version = new Version(VersionTag);
|
public static readonly Version Version = new Version(VersionTag);
|
||||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||||
@@ -44,7 +45,6 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
public static uint WindowRestoreMessage;
|
public static uint WindowRestoreMessage;
|
||||||
public static uint SubProcessMessage;
|
public static uint SubProcessMessage;
|
||||||
public static uint VideoPlayerMessage;
|
|
||||||
|
|
||||||
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
||||||
private static bool HasCleanedUp;
|
private static bool HasCleanedUp;
|
||||||
@@ -61,6 +61,10 @@ namespace TweetDuck{
|
|||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
||||||
|
#endif
|
||||||
|
|
||||||
Reporter = new Reporter(ErrorLogFilePath);
|
Reporter = new Reporter(ErrorLogFilePath);
|
||||||
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
||||||
}
|
}
|
||||||
@@ -70,9 +74,8 @@ namespace TweetDuck{
|
|||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
|
||||||
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
|
||||||
VideoPlayerMessage = NativeMethods.RegisterWindowMessage("TweetDuckVideoPlayer");
|
|
||||||
|
|
||||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||||
|
@@ -9,7 +9,7 @@ Emoji keyboard
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.2.2
|
1.3
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
enabled(){
|
enabled(){
|
||||||
|
this.ENABLE_CUSTOM_KEYBOARD = false;
|
||||||
|
|
||||||
this.selectedSkinTone = "";
|
this.selectedSkinTone = "";
|
||||||
this.currentKeywords = [];
|
this.currentKeywords = [];
|
||||||
|
|
||||||
@@ -30,16 +32,28 @@ enabled(){
|
|||||||
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
|
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
|
||||||
|
|
||||||
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
||||||
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
||||||
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
|
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
|
||||||
|
|
||||||
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }");
|
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }");
|
||||||
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
|
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
|
||||||
|
|
||||||
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }");
|
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }");
|
||||||
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
|
||||||
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
|
||||||
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
|
||||||
|
|
||||||
|
this.css.insert("#emoji-keyboard-tweet-input { padding: 0 !important; line-height: 18px }");
|
||||||
|
this.css.insert("#emoji-keyboard-tweet-input img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.25em }");
|
||||||
|
this.css.insert("#emoji-keyboard-tweet-input:empty::before { content: \"What's happening?\"; display: inline-block; color: #ced8de }");
|
||||||
|
|
||||||
|
this.css.insert(".js-docked-compose .compose-text-container.td-emoji-keyboard-swap .js-compose-text { position: absolute; z-index: -9999; left: 0; opacity: 0 }");
|
||||||
|
this.css.insert(".compose-text-container:not(.td-emoji-keyboard-swap) #emoji-keyboard-tweet-input { display: none; }");
|
||||||
|
|
||||||
|
this.css.insert(".js-compose-text { font-family: \"Twitter Color Emoji\", Helvetica, Arial, Verdana, sans-serif; }");
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
|
|
||||||
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
||||||
@@ -47,10 +61,10 @@ enabled(){
|
|||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
||||||
|
|
||||||
var dockedComposePanel = $(".js-docked-compose");
|
var maybeDockedComposePanel = $(".js-docked-compose");
|
||||||
|
|
||||||
if (dockedComposePanel.length){
|
if (maybeDockedComposePanel.length){
|
||||||
dockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyboard generation
|
// keyboard generation
|
||||||
@@ -58,7 +72,7 @@ enabled(){
|
|||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
this.currentSpanner = null;
|
this.currentSpanner = null;
|
||||||
|
|
||||||
var hideKeyboard = () => {
|
var hideKeyboard = (refocus) => {
|
||||||
$(this.currentKeyboard).remove();
|
$(this.currentKeyboard).remove();
|
||||||
this.currentKeyboard = null;
|
this.currentKeyboard = null;
|
||||||
|
|
||||||
@@ -70,7 +84,15 @@ enabled(){
|
|||||||
this.composePanelScroller.trigger("scroll");
|
this.composePanelScroller.trigger("scroll");
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
$(".js-compose-text").first().focus();
|
|
||||||
|
if (refocus){
|
||||||
|
if ($(".compose-text-container", ".js-docked-compose").hasClass("td-emoji-keyboard-swap")){
|
||||||
|
$("#emoji-keyboard-tweet-input").focus();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$(".js-compose-text", ".js-docked-compose").focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var generateEmojiHTML = skinTone => {
|
var generateEmojiHTML = skinTone => {
|
||||||
@@ -124,7 +146,7 @@ enabled(){
|
|||||||
updateFilters();
|
updateFilters();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.generateKeyboard = (input, left, top) => {
|
this.generateKeyboard = (left, top) => {
|
||||||
var outer = document.createElement("div");
|
var outer = document.createElement("div");
|
||||||
outer.classList.add("emoji-keyboard");
|
outer.classList.add("emoji-keyboard");
|
||||||
outer.style.left = left+"px";
|
outer.style.left = left+"px";
|
||||||
@@ -134,18 +156,10 @@ enabled(){
|
|||||||
keyboard.classList.add("emoji-keyboard-list");
|
keyboard.classList.add("emoji-keyboard-list");
|
||||||
|
|
||||||
keyboard.addEventListener("click", function(e){
|
keyboard.addEventListener("click", function(e){
|
||||||
if (e.target.tagName === "IMG"){
|
let ele = e.target;
|
||||||
var val = input.val();
|
|
||||||
var inserted = e.target.getAttribute("alt");
|
|
||||||
var posStart = input[0].selectionStart;
|
|
||||||
var posEnd = input[0].selectionEnd;
|
|
||||||
|
|
||||||
input.val(val.slice(0, posStart)+inserted+val.slice(posStart));
|
if (ele.tagName === "IMG"){
|
||||||
input.trigger("change");
|
insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt"));
|
||||||
input.focus();
|
|
||||||
|
|
||||||
input[0].selectionStart = posStart+inserted.length;
|
|
||||||
input[0].selectionEnd = posEnd+inserted.length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -204,7 +218,43 @@ enabled(){
|
|||||||
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
||||||
};
|
};
|
||||||
|
|
||||||
// event handlers
|
var focusWithCaretAtEnd = () => {
|
||||||
|
let range = document.createRange();
|
||||||
|
range.selectNodeContents(me.composeInputNew[0]);
|
||||||
|
range.collapse(false);
|
||||||
|
|
||||||
|
let sel = window.getSelection();
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
};
|
||||||
|
|
||||||
|
var insertEmoji = (src, alt) => {
|
||||||
|
if (this.ENABLE_CUSTOM_KEYBOARD){
|
||||||
|
replaceEditor(true);
|
||||||
|
|
||||||
|
if (!hasSelectionInEditor()){
|
||||||
|
focusWithCaretAtEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.execCommand("insertHTML", false, `<img src="${src}" alt="${alt}">`);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
let input = $(".js-compose-text", ".js-docked-compose");
|
||||||
|
|
||||||
|
let val = input.val();
|
||||||
|
let posStart = input[0].selectionStart;
|
||||||
|
let posEnd = input[0].selectionEnd;
|
||||||
|
|
||||||
|
input.val(val.slice(0, posStart)+alt+val.slice(posEnd));
|
||||||
|
input.trigger("change");
|
||||||
|
input.focus();
|
||||||
|
|
||||||
|
input[0].selectionStart = posStart+alt.length;
|
||||||
|
input[0].selectionEnd = posStart+alt.length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// general event handlers
|
||||||
|
|
||||||
this.emojiKeyboardButtonClickEvent = function(e){
|
this.emojiKeyboardButtonClickEvent = function(e){
|
||||||
if (me.currentKeyboard){
|
if (me.currentKeyboard){
|
||||||
@@ -212,7 +262,7 @@ enabled(){
|
|||||||
$(this).blur();
|
$(this).blur();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
me.generateKeyboard($(".js-compose-text").first(), $(this).offset().left, getKeyboardTop());
|
me.generateKeyboard($(this).offset().left, getKeyboardTop());
|
||||||
$(this).addClass("is-selected");
|
$(this).addClass("is-selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,14 +280,14 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.documentClickEvent = function(e){
|
this.documentClickEvent = function(e){
|
||||||
if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
|
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.documentKeyEvent = function(e){
|
this.documentKeyEvent = function(e){
|
||||||
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
if (me.currentKeyboard && e.keyCode === 27){ // escape
|
||||||
hideKeyboard();
|
hideKeyboard(true);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -246,7 +296,149 @@ enabled(){
|
|||||||
if (me.currentKeyboard){
|
if (me.currentKeyboard){
|
||||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// new editor event handlers
|
||||||
|
|
||||||
|
var prevOldInputVal = "";
|
||||||
|
var isEditorActive = false;
|
||||||
|
|
||||||
|
var hasSelectionInEditor = function(){
|
||||||
|
let sel = window.getSelection();
|
||||||
|
return sel.anchorNode && $(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length;
|
||||||
|
};
|
||||||
|
|
||||||
|
var migrateEditorText = function(){
|
||||||
|
let selStart = me.composeInputOrig[0].selectionStart;
|
||||||
|
let selEnd = me.composeInputOrig[0].selectionEnd;
|
||||||
|
|
||||||
|
let val = me.composeInputOrig.val();
|
||||||
|
let splitStart = val.substring(0, selStart).split("\n");
|
||||||
|
let splitEnd = val.substring(0, selEnd).split("\n");
|
||||||
|
|
||||||
|
me.composeInputNew.text(val);
|
||||||
|
me.composeInputNew.html(me.composeInputNew.text().replace(/^(.*?)$/gm, "<div>$1</div>").replace(/<div><\/div>/g, "<div><br></div>"));
|
||||||
|
|
||||||
|
let sel = window.getSelection();
|
||||||
|
let range = document.createRange();
|
||||||
|
|
||||||
|
if (me.composeInputNew[0].children.length === 0){
|
||||||
|
focusWithCaretAtEnd();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
me.composeInputNew.focus();
|
||||||
|
range.setStart(me.composeInputNew[0].children[splitStart.length-1].firstChild, splitStart.length > 1 ? selStart-val.lastIndexOf("\n", selStart)-1 : selStart);
|
||||||
|
range.setEnd(me.composeInputNew[0].children[splitEnd.length-1].firstChild, splitEnd.length > 1 ? selEnd-val.lastIndexOf("\n", selEnd)-1 : selEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
};
|
||||||
|
|
||||||
|
var replaceEditor = function(useCustom){
|
||||||
|
if (useCustom && !isEditorActive){
|
||||||
|
isEditorActive = true;
|
||||||
|
}
|
||||||
|
else if (!useCustom && isEditorActive){
|
||||||
|
isEditorActive = false;
|
||||||
|
}
|
||||||
|
else return;
|
||||||
|
|
||||||
|
$(".compose-text-container", ".js-docked-compose").toggleClass("td-emoji-keyboard-swap", isEditorActive);
|
||||||
|
|
||||||
|
if (isEditorActive){
|
||||||
|
migrateEditorText();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
me.composeInputOrig.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.composeOldInputFocusEvent = function(){
|
||||||
|
if (!isEditorActive){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = $(this).val();
|
||||||
|
|
||||||
|
if (val.length === 0){
|
||||||
|
replaceEditor(false);
|
||||||
|
}
|
||||||
|
else if (val != prevOldInputVal){
|
||||||
|
setTimeout(migrateEditorText, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
focusWithCaretAtEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevOldInputVal = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
var allowedShortcuts = [ 65 /* A */, 67 /* C */, 86 /* V */, 89 /* Y */, 90 /* Z */ ];
|
||||||
|
|
||||||
|
this.composeInputKeyEvent = function(e){
|
||||||
|
if (e.type === "keydown" && (e.ctrlKey || e.metaKey)){
|
||||||
|
if (e.keyCode === 13){ // enter
|
||||||
|
$(".js-send-button", ".js-docked-compose").click();
|
||||||
|
}
|
||||||
|
else if (e.keyCode >= 48 && !allowedShortcuts.includes(e.keyCode)){
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.keyCode !== 27){ // escape
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.composeInputUpdateEvent = function(){
|
||||||
|
let clone = $(this).clone();
|
||||||
|
|
||||||
|
clone.children("div").each(function(){
|
||||||
|
let ele = $(this)[0];
|
||||||
|
ele.outerHTML = "\n"+ele.innerHTML;
|
||||||
|
});
|
||||||
|
|
||||||
|
clone.children("img").each(function(){
|
||||||
|
let ele = $(this)[0];
|
||||||
|
ele.outerHTML = ele.getAttribute("alt");
|
||||||
|
});
|
||||||
|
|
||||||
|
me.composeInputOrig.val(prevOldInputVal = clone.text());
|
||||||
|
me.composeInputOrig.trigger("change");
|
||||||
|
|
||||||
|
if (prevOldInputVal.length === 0){
|
||||||
|
replaceEditor(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO if (!emoji.length){
|
||||||
|
let sel = window.getSelection();
|
||||||
|
let selStart = -1, selEnd = -1;
|
||||||
|
|
||||||
|
if ($(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length && sel.rangeCount === 1){
|
||||||
|
let range = sel.getRangeAt(0);
|
||||||
|
// TODO figure out offset
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceEditor(false);
|
||||||
|
me.composeInputOrig.focus();
|
||||||
|
|
||||||
|
if (selStart !== -1){
|
||||||
|
me.composeInputOrig[0].selectionStart = selStart;
|
||||||
|
me.composeInputOrig[0].selectionEnd = selEnd;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
this.composeInputPasteEvent = function(e){ // contenteditable with <img alt> handles copying just fine
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let text = e.originalEvent.clipboardData.getData("text/plain");
|
||||||
|
text && document.execCommand("insertText", false, text);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO handle @ and hashtags
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
@@ -261,6 +453,19 @@ ready(){
|
|||||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
|
||||||
|
this.composeInputOrig = $(".js-compose-text", ".js-docked-compose").first();
|
||||||
|
this.composeInputNew = $('<div id="emoji-keyboard-tweet-input" contenteditable="true" class="compose-text txt-size--14 scroll-v scroll-styled-v scroll-styled-h scroll-alt td-detect-image-paste"></div>').insertAfter(this.composeInputOrig);
|
||||||
|
|
||||||
|
if (this.ENABLE_CUSTOM_KEYBOARD){
|
||||||
|
this.composeInputOrig.on("focus", this.composeOldInputFocusEvent);
|
||||||
|
|
||||||
|
this.composeInputNew.on("keydown keypress keyup", this.composeInputKeyEvent);
|
||||||
|
this.composeInputNew.on("input", this.composeInputUpdateEvent);
|
||||||
|
this.composeInputNew.on("paste", this.composeInputPasteEvent);
|
||||||
|
}
|
||||||
|
|
||||||
// HTML generation
|
// HTML generation
|
||||||
|
|
||||||
var convUnicode = function(codePt){
|
var convUnicode = function(codePt){
|
||||||
@@ -280,12 +485,14 @@ ready(){
|
|||||||
|
|
||||||
// declaration inserters
|
// declaration inserters
|
||||||
|
|
||||||
|
let mapUnicode = pt => convUnicode(parseInt(pt, 16));
|
||||||
|
|
||||||
let addDeclaration1 = decl => {
|
let addDeclaration1 = decl => {
|
||||||
this.emojiData1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
this.emojiData1.push(decl.split(" ").map(mapUnicode).join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
let addDeclaration2 = (tone, decl) => {
|
let addDeclaration2 = (tone, decl) => {
|
||||||
let gen = decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("");
|
let gen = decl.split(" ").map(mapUnicode).join("");
|
||||||
|
|
||||||
if (tone === null){
|
if (tone === null){
|
||||||
for(let skinTone of this.skinToneList){
|
for(let skinTone of this.skinToneList){
|
||||||
@@ -298,7 +505,7 @@ ready(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
let addDeclaration3 = decl => {
|
let addDeclaration3 = decl => {
|
||||||
this.emojiData3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
|
this.emojiData3.push(decl.split(" ").map(mapUnicode).join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
// line reading
|
// line reading
|
||||||
@@ -372,6 +579,9 @@ disabled(){
|
|||||||
$(this.currentSpanner).remove();
|
$(this.currentSpanner).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.composeInputNew.remove();
|
||||||
|
|
||||||
|
this.composeInputOrig.off("focus", this.composeOldInputFocusEvent);
|
||||||
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||||
|
@@ -8,7 +8,7 @@ Templates
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.0.1
|
1.0.2
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -267,7 +267,7 @@ enabled(){
|
|||||||
<ul></ul>
|
<ul></ul>
|
||||||
|
|
||||||
<div class="templates-modal-bottom">
|
<div class="templates-modal-bottom">
|
||||||
<button data-action="close" class="btn" style="background-color:#d2d2d2;border-color:#ccd0d2"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
|
<button data-action="close" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
|
||||||
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
12
Resources/PostBuild.ps1
Normal file
12
Resources/PostBuild.ps1
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Param([Parameter(Mandatory = $True, Position = 1)][string] $dir)
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Set-Location $dir
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Include *.js, *.html -Recurse){
|
||||||
|
$lines = Get-Content -Path $file.FullName
|
||||||
|
$lines = ($lines | % { $_.TrimStart() }) -Replace '^(.*?)((?<=^|[;{}()] )//\s.*)?$', '$1'
|
||||||
|
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
||||||
|
|
||||||
|
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
||||||
|
}
|
@@ -141,7 +141,8 @@
|
|||||||
startRecentTweetTimer();
|
startRecentTweetTimer();
|
||||||
|
|
||||||
if (column.model.getHasNotification()){
|
if (column.model.getHasNotification()){
|
||||||
let previews = $TDX.notificationMediaPreviews;
|
let sensitive = (tweet.getRelatedTweet() && tweet.getRelatedTweet().possiblySensitive || (tweet.quotedTweet && tweet.quotedTweet.possiblySensitive));
|
||||||
|
let previews = $TDX.notificationMediaPreviews && !sensitive;
|
||||||
|
|
||||||
let html = $(tweet.render({
|
let html = $(tweet.render({
|
||||||
withFooter: false,
|
withFooter: false,
|
||||||
@@ -184,12 +185,19 @@
|
|||||||
|
|
||||||
if (type === "follow"){
|
if (type === "follow"){
|
||||||
html.find(".js-user-actions-menu").parent().remove();
|
html.find(".js-user-actions-menu").parent().remove();
|
||||||
|
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
|
||||||
}
|
}
|
||||||
else if (type.includes("list_member")){
|
else if (type.includes("list_member")){
|
||||||
html.find(".activity-header").first().css("margin-top", "2px");
|
html.find(".activity-header").css("margin-top", "2px");
|
||||||
html.find(".avatar").first().css("margin-bottom", "0");
|
html.find(".avatar").first().css("margin-bottom", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sensitive){
|
||||||
|
html.find(".media-badge").each(function(){
|
||||||
|
$(this)[0].lastChild.textContent += " (possibly sensitive)";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let source = tweet.getRelatedTweet();
|
let source = tweet.getRelatedTweet();
|
||||||
let duration = source ? source.text.length+(source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
let duration = source ? source.text.length+(source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
||||||
let tweetUrl = source ? source.getChirpURL() : "";
|
let tweetUrl = source ? source.getChirpURL() : "";
|
||||||
@@ -526,7 +534,7 @@
|
|||||||
onAppReady.push(function(){
|
onAppReady.push(function(){
|
||||||
var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
||||||
|
|
||||||
app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(e){
|
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
|
||||||
for(let item of e.originalEvent.clipboardData.items){
|
for(let item of e.originalEvent.clipboardData.items){
|
||||||
if (item.type.startsWith("image/")){
|
if (item.type.startsWith("image/")){
|
||||||
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
||||||
@@ -719,8 +727,8 @@
|
|||||||
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
|
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
|
||||||
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
|
||||||
|
|
||||||
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, media previews
|
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, media previews
|
||||||
addRule(".dropdown-menu, .list-item-last, .quoted-tweet { border-radius: 0 !important; }"); // square-ify dropdowns, quoted tweets, and account selectors
|
addRule(".compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet { border-radius: 0 !important; }"); // square-ify dropdowns, quoted tweets, and account selectors
|
||||||
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
|
||||||
|
|
||||||
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
|
||||||
@@ -749,6 +757,7 @@
|
|||||||
|
|
||||||
addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications
|
addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications
|
||||||
addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications
|
addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications
|
||||||
|
addRule(".account-bio.padding-t--5 { padding-top: 2px !important; }"); // decrease padding on follow notifications
|
||||||
|
|
||||||
addRule("html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { color: #000; border-color: #000; opacity: 0.25; }"); // make follow notification button nicer
|
addRule("html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { color: #000; border-color: #000; opacity: 0.25; }"); // make follow notification button nicer
|
||||||
addRule("html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu { color: #fff; border-color: #fff; opacity: 0.25; }"); // make follow notification button nicer
|
addRule("html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu { color: #fff; border-color: #fff; opacity: 0.25; }"); // make follow notification button nicer
|
||||||
@@ -762,6 +771,7 @@
|
|||||||
addRule("#td-compose-drawer-pin { margin: 17px 4px 0 0; transition: transform 0.1s ease; fill: #fff; float: right; cursor: pointer; }"); // replace 'stay open' checkbox with a pin icon
|
addRule("#td-compose-drawer-pin { margin: 17px 4px 0 0; transition: transform 0.1s ease; fill: #fff; float: right; cursor: pointer; }"); // replace 'stay open' checkbox with a pin icon
|
||||||
addRule(".js-docked-compose footer { display: none; }"); // replace 'stay open' checkbox with a pin icon
|
addRule(".js-docked-compose footer { display: none; }"); // replace 'stay open' checkbox with a pin icon
|
||||||
addRule(".compose-content { bottom: 0 !important; }"); // replace 'stay open' checkbox with a pin icon
|
addRule(".compose-content { bottom: 0 !important; }"); // replace 'stay open' checkbox with a pin icon
|
||||||
|
addRule(".js-docked-compose .js-drawer-close { margin: 20px 0 0 !important; }"); // fix close drawer button because twitter is fucking incompetent
|
||||||
|
|
||||||
window.TDGF_reinjectCustomCSS = function(styles){
|
window.TDGF_reinjectCustomCSS = function(styles){
|
||||||
$("#tweetduck-custom-css").remove();
|
$("#tweetduck-custom-css").remove();
|
||||||
@@ -797,7 +807,6 @@
|
|||||||
var playVideo = function(url){
|
var playVideo = function(url){
|
||||||
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
||||||
$TD.playVideo(null);
|
$TD.playVideo(null);
|
||||||
$(this).remove();
|
|
||||||
}).appendTo(app);
|
}).appendTo(app);
|
||||||
|
|
||||||
$TD.playVideo(url);
|
$TD.playVideo(url);
|
||||||
|
@@ -143,8 +143,8 @@
|
|||||||
display: list-item;
|
display: list-item;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tweetduck-changelog .l2 {
|
#tweetduck-changelog p.l2 {
|
||||||
margin-left: 50px;
|
margin-left: 50px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tweetduck-changelog a {
|
#tweetduck-changelog a {
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
||||||
.replace(/\*(.*?)\*/g, "<em>$1</em>")
|
.replace(/\*(.*?)\*/g, "<em>$1</em>")
|
||||||
.replace(/`(.*?)`/g, "<code>$1</code>")
|
.replace(/`(.*?)`/g, "<code>$1</code>")
|
||||||
.replace(/\[(.*?)\]\((.*?)\)/g, "<a href='$2'>$1</a>")
|
.replace(/\[(.*?)\]\((.*?)\)/g, "<a href='$2' target='_blank'>$1</a>")
|
||||||
.replace(/^([a-z0-9].*?)$/gmi, "<p>$1</p>")
|
.replace(/^([a-z0-9].*?)$/gmi, "<p>$1</p>")
|
||||||
.replace(/\n\r?\n\r?/g, "<br>");
|
.replace(/\n\r?\n\r?/g, "<br>");
|
||||||
};
|
};
|
||||||
|
@@ -98,6 +98,7 @@
|
|||||||
<Compile Include="Core\FormBrowser.Designer.cs">
|
<Compile Include="Core\FormBrowser.Designer.cs">
|
||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
||||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\General\RequestHandlerBase.cs" />
|
<Compile Include="Core\Handling\General\RequestHandlerBase.cs" />
|
||||||
@@ -323,6 +324,7 @@
|
|||||||
<None Include="Resources\icon-small.ico" />
|
<None Include="Resources\icon-small.ico" />
|
||||||
<None Include="Resources\icon-tray-new.ico" />
|
<None Include="Resources\icon-tray-new.ico" />
|
||||||
<None Include="Resources\icon-tray.ico" />
|
<None Include="Resources\icon-tray.ico" />
|
||||||
|
<None Include="Resources\PostBuild.ps1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources\Plugins\" />
|
<Folder Include="Resources\Plugins\" />
|
||||||
@@ -351,6 +353,10 @@
|
|||||||
<Project>{278b2d11-402d-44b6-b6a1-8fa67db65565}</Project>
|
<Project>{278b2d11-402d-44b6-b6a1-8fa67db65565}</Project>
|
||||||
<Name>TweetDuck.Video</Name>
|
<Name>TweetDuck.Video</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="lib\TweetLib.Communication\TweetLib.Communication.csproj">
|
||||||
|
<Project>{72473763-4b9d-4fb6-a923-9364b2680f06}</Project>
|
||||||
|
<Name>TweetLib.Communication</Name>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -378,7 +384,9 @@ if $(ConfigurationName) == Debug (
|
|||||||
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
||||||
mkdir "$(TargetDir)plugins\user\.debug"
|
mkdir "$(TargetDir)plugins\user\.debug"
|
||||||
xcopy "$(ProjectDir)Resources\Plugins\.debug\*" "$(TargetDir)plugins\user\.debug\" /E /Y
|
xcopy "$(ProjectDir)Resources\Plugins\.debug\*" "$(TargetDir)plugins\user\.debug\" /E /Y
|
||||||
)</PostBuildEvent>
|
)
|
||||||
|
|
||||||
|
powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild.ps1" "$(TargetDir)\"</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -402,4 +410,7 @@ if $(ConfigurationName) == Debug (
|
|||||||
<Delete Files="$(TargetDir)devtools_resources.pak" />
|
<Delete Files="$(TargetDir)devtools_resources.pak" />
|
||||||
<Delete Files="$(TargetDir)widevinecdmadapter.dll" />
|
<Delete Files="$(TargetDir)widevinecdmadapter.dll" />
|
||||||
</Target>
|
</Target>
|
||||||
|
<PropertyGroup>
|
||||||
|
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent>
|
||||||
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Audio", "lib\Tweet
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
@@ -38,6 +40,10 @@ Global
|
|||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
|
||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
|
||||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.Build.0 = Release|x86
|
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.Build.0 = Release|x86
|
||||||
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
34
lib/TweetLib.Communication/Comms.cs
Normal file
34
lib/TweetLib.Communication/Comms.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using TweetLib.Communication.Utils;
|
||||||
|
|
||||||
|
namespace TweetLib.Communication{
|
||||||
|
public static class Comms{
|
||||||
|
public static void SendMessage(IntPtr hWnd, uint msg, uint wParam, int lParam){
|
||||||
|
NativeMethods.SendMessage(hWnd, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam){
|
||||||
|
NativeMethods.SendMessage(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PostMessage(IntPtr hWnd, uint msg, uint wParam, int lParam){
|
||||||
|
NativeMethods.PostMessage(hWnd, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam){
|
||||||
|
NativeMethods.PostMessage(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
||||||
|
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BroadcastMessage(uint msg, UIntPtr wParam, IntPtr lParam){
|
||||||
|
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint RegisterMessage(string name){
|
||||||
|
return NativeMethods.RegisterWindowMessage(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
lib/TweetLib.Communication/DuplexPipe.cs
Normal file
108
lib/TweetLib.Communication/DuplexPipe.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace TweetLib.Communication{
|
||||||
|
public abstract class DuplexPipe : IDisposable{
|
||||||
|
private const string Separator = "\x1F";
|
||||||
|
|
||||||
|
public static Server CreateServer(){
|
||||||
|
return new Server();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Client CreateClient(string token){
|
||||||
|
int space = token.IndexOf(' ');
|
||||||
|
return new Client(token.Substring(0, space), token.Substring(space+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly PipeStream pipeIn;
|
||||||
|
protected readonly PipeStream pipeOut;
|
||||||
|
|
||||||
|
private readonly Thread readerThread;
|
||||||
|
private readonly StreamWriter writerStream;
|
||||||
|
|
||||||
|
public event EventHandler<PipeReadEventArgs> DataIn;
|
||||||
|
|
||||||
|
protected DuplexPipe(PipeStream pipeIn, PipeStream pipeOut){
|
||||||
|
this.pipeIn = pipeIn;
|
||||||
|
this.pipeOut = pipeOut;
|
||||||
|
|
||||||
|
this.readerThread = new Thread(ReaderThread){
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
|
||||||
|
this.readerThread.Start();
|
||||||
|
this.writerStream = new StreamWriter(this.pipeOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReaderThread(){
|
||||||
|
using(StreamReader read = new StreamReader(pipeIn)){
|
||||||
|
string data;
|
||||||
|
|
||||||
|
while((data = read.ReadLine()) != null){
|
||||||
|
DataIn?.Invoke(this, new PipeReadEventArgs(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string key){
|
||||||
|
writerStream.WriteLine(key);
|
||||||
|
writerStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string key, string data){
|
||||||
|
writerStream.WriteLine(string.Concat(key, Separator, data));
|
||||||
|
writerStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
try{
|
||||||
|
readerThread.Abort();
|
||||||
|
}catch{
|
||||||
|
// /shrug
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeIn.Dispose();
|
||||||
|
writerStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Server : DuplexPipe{
|
||||||
|
private AnonymousPipeServerStream ServerPipeIn => (AnonymousPipeServerStream)pipeIn;
|
||||||
|
private AnonymousPipeServerStream ServerPipeOut => (AnonymousPipeServerStream)pipeOut;
|
||||||
|
|
||||||
|
internal Server() : base(new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable), new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)){}
|
||||||
|
|
||||||
|
public string GenerateToken(){
|
||||||
|
return ServerPipeIn.GetClientHandleAsString()+" "+ServerPipeOut.GetClientHandleAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeToken(){
|
||||||
|
ServerPipeIn.DisposeLocalCopyOfClientHandle();
|
||||||
|
ServerPipeOut.DisposeLocalCopyOfClientHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Client : DuplexPipe{
|
||||||
|
internal Client(string handleOut, string handleIn) : base(new AnonymousPipeClientStream(PipeDirection.In, handleIn), new AnonymousPipeClientStream(PipeDirection.Out, handleOut)){}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PipeReadEventArgs : EventArgs{
|
||||||
|
public string Key { get; }
|
||||||
|
public string Data { get; }
|
||||||
|
|
||||||
|
internal PipeReadEventArgs(string line){
|
||||||
|
int separatorIndex = line.IndexOf(Separator, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
if (separatorIndex == -1){
|
||||||
|
Key = line;
|
||||||
|
Data = string.Empty;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Key = line.Substring(0, separatorIndex);
|
||||||
|
Data = line.Substring(separatorIndex+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
lib/TweetLib.Communication/Properties/AssemblyInfo.cs
Normal file
35
lib/TweetLib.Communication/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TweetDuck Communication Library")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TweetDuck Communication Library")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("72473763-4b9d-4fb6-a923-9364b2680f06")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
44
lib/TweetLib.Communication/TweetLib.Communication.csproj
Normal file
44
lib/TweetLib.Communication/TweetLib.Communication.csproj
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{72473763-4B9D-4FB6-A923-9364B2680F06}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>TweetLib.Communication</RootNamespace>
|
||||||
|
<AssemblyName>TweetLib.Communication</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="DuplexPipe.cs" />
|
||||||
|
<Compile Include="Utils\NativeMethods.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Comms.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
@@ -1,10 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetLib.Communication.Utils{
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
@@ -1,7 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.BrowserSubprocess;
|
using CefSharp.BrowserSubprocess;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetDuck.Browser{
|
||||||
static class Program{
|
static class Program{
|
||||||
@@ -26,7 +27,7 @@ namespace TweetDuck.Browser{
|
|||||||
base.OnBrowserCreated(wrapper);
|
base.OnBrowserCreated(wrapper);
|
||||||
|
|
||||||
using(Process me = Process.GetCurrentProcess()){
|
using(Process me = Process.GetCurrentProcess()){
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, NativeMethods.RegisterWindowMessage("TweetDuckSubProcess"), new UIntPtr((uint)me.Id), new IntPtr(wrapper.BrowserId));
|
Comms.BroadcastMessage(Comms.RegisterMessage("TweetDuckSubProcess"), (uint)me.Id, wrapper.BrowserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -31,10 +31,15 @@
|
|||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="NativeMethods.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\lib\TweetLib.Communication\TweetLib.Communication.csproj">
|
||||||
|
<Project>{72473763-4b9d-4fb6-a923-9364b2680f06}</Project>
|
||||||
|
<Name>TweetLib.Communication</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using WMPLib;
|
using WMPLib;
|
||||||
|
|
||||||
namespace TweetDuck.Video{
|
namespace TweetDuck.Video.Controls{
|
||||||
[DesignTimeVisible(true)]
|
[DesignTimeVisible(true)]
|
||||||
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
|
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
|
||||||
class ControlWMP : AxHost{
|
class ControlWMP : AxHost{
|
57
video/TweetDuck.Video/Controls/SeekBar.cs
Normal file
57
video/TweetDuck.Video/Controls/SeekBar.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TweetDuck.Video.Controls{
|
||||||
|
sealed class SeekBar : ProgressBar{
|
||||||
|
private readonly SolidBrush brushFore;
|
||||||
|
private readonly SolidBrush brushHover;
|
||||||
|
private readonly SolidBrush brushOverlap;
|
||||||
|
|
||||||
|
public SeekBar(){
|
||||||
|
brushFore = new SolidBrush(Color.White);
|
||||||
|
brushHover = new SolidBrush(Color.White);
|
||||||
|
brushOverlap = new SolidBrush(Color.White);
|
||||||
|
|
||||||
|
SetStyle(ControlStyles.UserPaint, true);
|
||||||
|
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e){
|
||||||
|
if (brushFore.Color != ForeColor){
|
||||||
|
brushFore.Color = ForeColor;
|
||||||
|
brushHover.Color = Color.FromArgb(128, ForeColor);
|
||||||
|
brushOverlap.Color = Color.FromArgb(80+ForeColor.R*11/16, 80+ForeColor.G*11/16, 80+ForeColor.B*11/16);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle rect = e.ClipRectangle;
|
||||||
|
Point cursor = PointToClient(Cursor.Position);
|
||||||
|
int width = rect.Width;
|
||||||
|
int progress = (int)(width*((double)Value/Maximum));
|
||||||
|
|
||||||
|
rect.Width = progress;
|
||||||
|
e.Graphics.FillRectangle(brushFore, rect);
|
||||||
|
|
||||||
|
if (cursor.X >= 0 && cursor.Y >= 0 && cursor.X <= width && cursor.Y <= rect.Height){
|
||||||
|
if (progress >= cursor.X){
|
||||||
|
rect.Width = cursor.X;
|
||||||
|
e.Graphics.FillRectangle(brushOverlap, rect);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
rect.X = progress;
|
||||||
|
rect.Width = cursor.X-rect.X;
|
||||||
|
e.Graphics.FillRectangle(brushHover, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing){
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (disposing){
|
||||||
|
brushFore.Dispose();
|
||||||
|
brushHover.Dispose();
|
||||||
|
brushOverlap.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,33 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace TweetDuck.Video{
|
|
||||||
sealed class FlatProgressBar : ProgressBar{
|
|
||||||
private readonly SolidBrush brush;
|
|
||||||
|
|
||||||
public FlatProgressBar(){
|
|
||||||
brush = new SolidBrush(Color.White);
|
|
||||||
|
|
||||||
SetStyle(ControlStyles.UserPaint, true);
|
|
||||||
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e){
|
|
||||||
if (brush.Color != ForeColor){
|
|
||||||
brush.Color = ForeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle rect = e.ClipRectangle;
|
|
||||||
rect.Width = (int)(rect.Width*((double)Value/Maximum));
|
|
||||||
e.Graphics.FillRectangle(brush, rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing){
|
|
||||||
base.Dispose(disposing);
|
|
||||||
|
|
||||||
if (disposing){
|
|
||||||
brush.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
4
video/TweetDuck.Video/FormPlayer.Designer.cs
generated
4
video/TweetDuck.Video/FormPlayer.Designer.cs
generated
@@ -27,7 +27,7 @@
|
|||||||
this.timerSync = new System.Windows.Forms.Timer(this.components);
|
this.timerSync = new System.Windows.Forms.Timer(this.components);
|
||||||
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
||||||
this.tablePanel = new System.Windows.Forms.TableLayoutPanel();
|
this.tablePanel = new System.Windows.Forms.TableLayoutPanel();
|
||||||
this.progressSeek = new TweetDuck.Video.FlatProgressBar();
|
this.progressSeek = new TweetDuck.Video.Controls.SeekBar();
|
||||||
this.labelTime = new System.Windows.Forms.Label();
|
this.labelTime = new System.Windows.Forms.Label();
|
||||||
this.timerData = new System.Windows.Forms.Timer(this.components);
|
this.timerData = new System.Windows.Forms.Timer(this.components);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
private System.Windows.Forms.Timer timerSync;
|
private System.Windows.Forms.Timer timerSync;
|
||||||
private System.Windows.Forms.TrackBar trackBarVolume;
|
private System.Windows.Forms.TrackBar trackBarVolume;
|
||||||
private System.Windows.Forms.TableLayoutPanel tablePanel;
|
private System.Windows.Forms.TableLayoutPanel tablePanel;
|
||||||
private FlatProgressBar progressSeek;
|
private Controls.SeekBar progressSeek;
|
||||||
private System.Windows.Forms.Label labelTime;
|
private System.Windows.Forms.Label labelTime;
|
||||||
private System.Windows.Forms.Timer timerData;
|
private System.Windows.Forms.Timer timerData;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Globalization;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Video.Controls;
|
||||||
|
using TweetLib.Communication;
|
||||||
using WMPLib;
|
using WMPLib;
|
||||||
|
|
||||||
namespace TweetDuck.Video{
|
namespace TweetDuck.Video{
|
||||||
@@ -10,6 +13,7 @@ namespace TweetDuck.Video{
|
|||||||
|
|
||||||
private readonly IntPtr ownerHandle;
|
private readonly IntPtr ownerHandle;
|
||||||
private readonly string videoUrl;
|
private readonly string videoUrl;
|
||||||
|
private readonly DuplexPipe pipe;
|
||||||
|
|
||||||
private readonly ControlWMP player;
|
private readonly ControlWMP player;
|
||||||
private bool wasCursorInside;
|
private bool wasCursorInside;
|
||||||
@@ -18,11 +22,13 @@ namespace TweetDuck.Video{
|
|||||||
|
|
||||||
private WindowsMediaPlayer Player => player.Ocx;
|
private WindowsMediaPlayer Player => player.Ocx;
|
||||||
|
|
||||||
public FormPlayer(IntPtr handle, int volume, string url){
|
public FormPlayer(IntPtr handle, int volume, string url, string token){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.ownerHandle = handle;
|
this.ownerHandle = handle;
|
||||||
this.videoUrl = url;
|
this.videoUrl = url;
|
||||||
|
this.pipe = DuplexPipe.CreateClient(token);
|
||||||
|
this.pipe.DataIn += pipe_DataIn;
|
||||||
|
|
||||||
player = new ControlWMP{
|
player = new ControlWMP{
|
||||||
Dock = DockStyle.Fill
|
Dock = DockStyle.Fill
|
||||||
@@ -52,6 +58,22 @@ namespace TweetDuck.Video{
|
|||||||
Player.URL = videoUrl;
|
Player.URL = videoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
||||||
|
switch(e.Key){
|
||||||
|
case "pause":
|
||||||
|
TogglePause();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "die":
|
||||||
|
timerSync.Stop();
|
||||||
|
Visible = false;
|
||||||
|
pipe.Write("rip");
|
||||||
|
|
||||||
|
Close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void player_PlayStateChange(int newState){
|
private void player_PlayStateChange(int newState){
|
||||||
WMPPlayState state = (WMPPlayState)newState;
|
WMPPlayState state = (WMPPlayState)newState;
|
||||||
|
|
||||||
@@ -132,7 +154,7 @@ namespace TweetDuck.Video{
|
|||||||
|
|
||||||
private void timerData_Tick(object sender, EventArgs e){
|
private void timerData_Tick(object sender, EventArgs e){
|
||||||
timerData.Stop();
|
timerData.Stop();
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, Program.VideoPlayerMessage, new UIntPtr((uint)trackBarVolume.Value), ownerHandle);
|
pipe.Write("vol", trackBarVolume.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void progressSeek_MouseDown(object sender, MouseEventArgs e){
|
private void progressSeek_MouseDown(object sender, MouseEventArgs e){
|
||||||
@@ -204,13 +226,17 @@ namespace TweetDuck.Video{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m.Msg == 0x0203 || (m.Msg == 0x0100 && m.WParam.ToInt32() == 0x20)){ // WM_LBUTTONDBLCLK, WM_KEYDOWN, VK_SPACE
|
else if (m.Msg == 0x0203){ // WM_LBUTTONDBLCLK
|
||||||
if (IsCursorOverVideo){
|
if (IsCursorOverVideo){
|
||||||
form.TogglePause();
|
form.TogglePause();
|
||||||
form.Player.fullScreen = !form.Player.fullScreen;
|
form.Player.fullScreen = !form.Player.fullScreen;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (m.Msg == 0x0100 && m.WParam.ToInt32() == 0x20){ // WM_KEYDOWN, VK_SPACE
|
||||||
|
form.TogglePause();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else if (m.Msg == 0x020B && ((m.WParam.ToInt32() >> 16) & 0xFFFF) == 1){ // WM_XBUTTONDOWN
|
else if (m.Msg == 0x020B && ((m.WParam.ToInt32() >> 16) & 0xFFFF) == 1){ // WM_XBUTTONDOWN
|
||||||
NativeMethods.SetForegroundWindow(form.ownerHandle);
|
NativeMethods.SetForegroundWindow(form.ownerHandle);
|
||||||
Environment.Exit(Program.CODE_USER_REQUESTED);
|
Environment.Exit(Program.CODE_USER_REQUESTED);
|
||||||
|
@@ -3,16 +3,8 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace TweetDuck.Video{
|
namespace TweetDuck.Video{
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
|
||||||
|
|
||||||
private const int GWL_HWNDPARENT = -8;
|
private const int GWL_HWNDPARENT = -8;
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern uint RegisterWindowMessage(string messageName);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetLib.Communication;
|
||||||
|
|
||||||
namespace TweetDuck.Video{
|
namespace TweetDuck.Video{
|
||||||
static class Program{
|
static class Program{
|
||||||
@@ -12,9 +13,6 @@ namespace TweetDuck.Video{
|
|||||||
public const int CODE_OWNER_GONE = 5;
|
public const int CODE_OWNER_GONE = 5;
|
||||||
public const int CODE_USER_REQUESTED = 6;
|
public const int CODE_USER_REQUESTED = 6;
|
||||||
|
|
||||||
private static uint? message;
|
|
||||||
public static uint VideoPlayerMessage => message ?? (message = NativeMethods.RegisterWindowMessage("TweetDuckVideoPlayer")).Value;
|
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
private static int Main(string[] args){
|
private static int Main(string[] args){
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
@@ -23,17 +21,19 @@ namespace TweetDuck.Video{
|
|||||||
IntPtr ownerHandle;
|
IntPtr ownerHandle;
|
||||||
int defaultVolume;
|
int defaultVolume;
|
||||||
string videoUrl;
|
string videoUrl;
|
||||||
|
string pipeToken;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture));
|
ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture));
|
||||||
defaultVolume = int.Parse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture);
|
defaultVolume = int.Parse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||||
videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri;
|
videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri;
|
||||||
|
pipeToken = args[3];
|
||||||
}catch{
|
}catch{
|
||||||
return CODE_INVALID_ARGS;
|
return CODE_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl));
|
Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Console.Out.WriteLine(e.Message);
|
Console.Out.WriteLine(e.Message);
|
||||||
return CODE_LAUNCH_FAIL;
|
return CODE_LAUNCH_FAIL;
|
||||||
|
@@ -44,10 +44,10 @@
|
|||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ControlWMP.cs">
|
<Compile Include="Controls\ControlWMP.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="FlatProgressBar.cs">
|
<Compile Include="Controls\SeekBar.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="FormPlayer.cs">
|
<Compile Include="FormPlayer.cs">
|
||||||
@@ -74,5 +74,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Resources\icon.ico" />
|
<Content Include="Resources\icon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\lib\TweetLib.Communication\TweetLib.Communication.csproj">
|
||||||
|
<Project>{72473763-4b9d-4fb6-a923-9364b2680f06}</Project>
|
||||||
|
<Name>TweetLib.Windows</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
Reference in New Issue
Block a user