1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 19:32:10 +02:00

Compare commits

...

27 Commits
1.8.6 ... 1.8.7

Author SHA1 Message Date
68dca6e3d9 Fix spacebar not toggling video pause when the main app was focused 2017-08-21 14:14:38 +02:00
017f883e0b Disable custom emoji input, fix selection handling and support twemoji font if installed 2017-08-21 13:37:21 +02:00
77b5c95f75 Add basic js minification (trim whitespace and remove single line comments) 2017-08-21 09:41:15 +02:00
9d052c8339 Update close button fix to only affect New Tweet drawer 2017-08-21 02:17:48 +02:00
d67623a657 Tweak follow notification padding in the browser 2017-08-21 01:52:19 +02:00
c740b3dd46 What the fuck are you doing Twitter 2017-08-21 01:35:53 +02:00
2ef5f7f96f Fix border radius on media previews in tweet detail 2017-08-16 18:27:44 +02:00
404568d795 Fix pre-build powershell command causing build error 2017-08-16 18:23:38 +02:00
b5a6337a0c Update custom CSS to work better with recent TweetDeck changes 2017-08-14 17:15:18 +02:00
82170c3fbd Fix sensitive media in notification previews and tweak follow notification padding 2017-08-14 16:12:34 +02:00
e6d6275fcc Work on emoji keyboard contenteditable fixes (selection, focus, editor migration) 2017-08-14 15:37:55 +02:00
97c865a127 Make emoji editor only show after adding emoji, fix minor UI issues 2017-08-14 04:22:13 +02:00
1ff21f0ee0 Make emoji keyboard replace tweet input with one that displays emoji
Closes #146
2017-08-14 00:47:08 +02:00
2a3dca4467 Rewrite video player to use duplex pipe for process communication 2017-08-13 17:52:46 +02:00
d4ecfcceec Tweak DuplexPipe to set key instead of data when separator is missing 2017-08-13 17:31:58 +02:00
ec5d503e4d Make DuplexPipe data serialized as key/value pairs 2017-08-13 17:23:23 +02:00
346391ca2d Remove unused 'using' statements for the billionth time 2017-08-13 16:55:08 +02:00
9074cdf340 Add a hover effect to video player seek bar 2017-08-13 16:46:33 +02:00
2fcf3604a8 Move video player form controls to a different namespace 2017-08-13 16:14:46 +02:00
34e5185fa1 Fuck localized .NET exceptions 2017-08-13 15:53:39 +02:00
e09e0e69ca Fuck browser process when building the project 2017-08-13 15:50:43 +02:00
963c98e588 Move interprocess comms to a separate project & implement duplex pipe 2017-08-13 15:20:04 +02:00
92acb823a4 Implement a duplex anonymous pipe in TweetLib.Communication 2017-08-13 15:14:17 +02:00
b967b1288f Move process communication to a separate project 2017-08-13 13:54:34 +02:00
1db271ce90 Fix spacebar triggering fullscreen in video player 2017-08-13 00:23:08 +02:00
58c64025e3 Fix level 2 lists and links in update changelog modal 2017-08-12 23:52:38 +02:00
643a7a87aa Release 1.8.6 2017-08-12 23:39:41 +02:00
31 changed files with 745 additions and 139 deletions

View File

@@ -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;

View File

@@ -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){

View File

@@ -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()");
} }

View 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;
}
}
}

View File

@@ -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){
if (isClosing){
Destroy();
isClosing = false;
}
else{
isClosing = true;
currentProcess.Exited -= process_Exited; 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);
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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
View 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)
}

View File

@@ -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);

View File

@@ -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>");
}; };

View File

@@ -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>

View File

@@ -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

View 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);
}
}
}

View 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);
}
}
}
}
}

View 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")]

View 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>

View File

@@ -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);

View File

@@ -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);
} }
} }
} }

View File

@@ -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"

View File

@@ -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{

View 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();
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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>