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

Compare commits

..

8 Commits

17 changed files with 175 additions and 72 deletions

View File

@@ -7,6 +7,7 @@ namespace TweetDuck.Configuration{
public const string ArgDataFolder = "-datafolder"; public const string ArgDataFolder = "-datafolder";
public const string ArgLogging = "-log"; public const string ArgLogging = "-log";
public const string ArgIgnoreGDPR = "-nogdpr"; public const string ArgIgnoreGDPR = "-nogdpr";
public const string ArgFreeze = "-freeze";
// internal args // internal args
public const string ArgRestart = "-restart"; public const string ArgRestart = "-restart";

View File

@@ -34,7 +34,7 @@ namespace TweetDuck.Configuration.Instance{
LoadInternal(attempt > 0); LoadInternal(attempt > 0);
if (firstException != null){ // silently log exception that caused a backup restore if (firstException != null){ // silently log exception that caused a backup restore
Program.Reporter.Log(firstException.ToString()); Program.Reporter.LogImportant(firstException.ToString());
} }
return; return;

View File

@@ -124,7 +124,7 @@ namespace TweetDuck.Configuration{
try{ try{
File.Delete(file); File.Delete(file);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.Log(e.ToString()); Program.Reporter.LogImportant(e.ToString());
return false; return false;
} }
} }

View File

@@ -0,0 +1,47 @@
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{
class KeyboardHandlerBase : IKeyboardHandler{
protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){
if (BrowserUtils.HasDevTools){
browser.ShowDevTools();
}
else{
browserControl.AsControl().InvokeSafe(() => {
string extraMessage;
if (Program.IsPortable){
extraMessage = "Please download the portable installer, select the folder with your current installation of TweetDuck Portable, and tick 'Install dev tools' during the installation process.";
}
else{
extraMessage = "Please download the installer, and tick 'Install dev tools' during the installation process. The installer will automatically find and update your current installation of TweetDuck.";
}
FormMessage.Information("Dev Tools", "You do not have dev tools installed. "+extraMessage, FormMessage.OK);
});
}
return true;
}
return false;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
}
return false;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -2,19 +2,19 @@
using CefSharp; using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class KeyboardHandlerBrowser : IKeyboardHandler{ sealed class KeyboardHandlerBrowser : KeyboardHandlerBase{
private readonly FormBrowser form; private readonly FormBrowser form;
public KeyboardHandlerBrowser(FormBrowser form){ public KeyboardHandlerBrowser(FormBrowser form){
this.form = form; this.form = form;
} }
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){ protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode); if (base.HandleRawKey(browserControl, browser, key, modifiers)){
} return true;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){ return form.ProcessBrowserKey(key);
return false;
} }
} }
} }

View File

@@ -4,7 +4,7 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Handling { namespace TweetDuck.Core.Handling {
sealed class KeyboardHandlerNotification : IKeyboardHandler{ sealed class KeyboardHandlerNotification : KeyboardHandlerBase{
private readonly FormNotificationBase notification; private readonly FormNotificationBase notification;
public KeyboardHandlerNotification(FormNotificationBase notification){ public KeyboardHandlerNotification(FormNotificationBase notification){
@@ -15,31 +15,30 @@ namespace TweetDuck.Core.Handling {
notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger); notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
} }
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){ protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){ if (base.HandleRawKey(browserControl, browser, key, modifiers)){
switch((Keys)windowsKeyCode){ return true;
case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
TriggerKeyboardShortcutAnalytics();
return true;
case Keys.Escape:
notification.InvokeAsyncSafe(notification.HideNotification);
TriggerKeyboardShortcutAnalytics();
return true;
case Keys.Space:
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
TriggerKeyboardShortcutAnalytics();
return true;
}
} }
return false; switch(key){
} case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
TriggerKeyboardShortcutAnalytics();
return true;
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){ case Keys.Escape:
return false; notification.InvokeAsyncSafe(notification.HideNotification);
TriggerKeyboardShortcutAnalytics();
return true;
case Keys.Space:
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
TriggerKeyboardShortcutAnalytics();
return true;
default:
return false;
}
} }
} }
} }

View File

@@ -1,20 +1,45 @@
// Uncomment to force TweetDeck to load a predefined version of the vendor/bundle scripts and stylesheets using System;
// #define FREEZE_TWEETDECK_RESOURCES using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using CefSharp; using CefSharp;
using CefSharp.Handler; using CefSharp.Handler;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
#if FREEZE_TWEETDECK_RESOURCES
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
#endif
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class RequestHandlerBase : DefaultRequestHandler{ class RequestHandlerBase : DefaultRequestHandler{
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$", RegexOptions.Compiled);
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
public static void LoadResourceRewriteRules(string rules){
if (string.IsNullOrEmpty(rules)){
return;
}
TweetDeckHashes.Clear();
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
string[] split = rule.Split('=');
if (split.Length == 2){
string key = split[0];
string hash = split[1];
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
TweetDeckHashes.Add(key, hash);
}
else{
throw new ArgumentException("Invalid hash characters: "+rule);
}
}
else{
throw new ArgumentException("A rule must have exactly one '=' character: "+rule);
}
}
}
private readonly bool autoReload; private readonly bool autoReload;
public RequestHandlerBase(bool autoReload){ public RequestHandlerBase(bool autoReload){
@@ -35,33 +60,17 @@ namespace TweetDuck.Core.Handling{
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback); return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
} }
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
if (autoReload){
browser.Reload();
}
}
#if FREEZE_TWEETDECK_RESOURCES
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$", RegexOptions.Compiled);
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(2){
{ "vendor.js", "d897f6b9ed" },
{ "bundle.js", "851d3877b9" },
{ "vendor.css", "ce7cdd10b6" },
{ "bundle.css", "c339f07047" }
};
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){ public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet){ if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
string url = request.Url; string url = request.Url;
Match match = TweetDeckResourceUrl.Match(url); Match match = TweetDeckResourceUrl.Match(url);
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){ if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
if (match.Groups[2].Value == hash){ if (match.Groups[2].Value == hash){
Debug.WriteLine($"Accepting {url}"); Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
} }
else{ else{
Debug.WriteLine($"Rewriting {url} hash to {hash}"); Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3"); request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
return true; return true;
} }
@@ -70,6 +79,11 @@ namespace TweetDuck.Core.Handling{
return base.OnResourceResponse(browserControl, browser, frame, request, response); return base.OnResourceResponse(browserControl, browser, frame, request, response);
} }
#endif
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
if (autoReload){
browser.Reload();
}
}
} }
} }

View File

@@ -135,7 +135,7 @@ namespace TweetDuck.Core.Management{
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){ private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
if (!string.IsNullOrEmpty(e.Data)){ if (!string.IsNullOrEmpty(e.Data)){
Program.Reporter.Log("[VideoPlayer] "+e.Data); Program.Reporter.LogVerbose("[VideoPlayer] "+e.Data);
} }
} }

View File

@@ -70,6 +70,7 @@ namespace TweetDuck.Core.Other{
this.browser = new ChromiumWebBrowser(url){ this.browser = new ChromiumWebBrowser(url){
MenuHandler = new ContextMenuGuide(owner), MenuHandler = new ContextMenuGuide(owner),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBase(),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBase(true), RequestHandler = new RequestHandlerBase(true),
ResourceHandlerFactory = resourceHandlerFactory ResourceHandlerFactory = resourceHandlerFactory

View File

@@ -9,6 +9,7 @@ using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core; using TweetDuck.Core;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
@@ -20,7 +21,7 @@ namespace TweetDuck{
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.17.2"; public const string VersionTag = "1.17.3";
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable")); public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
@@ -129,6 +130,13 @@ namespace TweetDuck{
BrowserCache.TryClearNow(); BrowserCache.TryClearNow();
} }
try{
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze, null));
}catch(Exception e){
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK);
return;
}
BrowserCache.RefreshTimer(); BrowserCache.RefreshTimer();
CefSharpSettings.WcfEnabled = false; CefSharpSettings.WcfEnabled = false;

View File

@@ -4,6 +4,7 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
namespace TweetDuck{ namespace TweetDuck{
@@ -22,7 +23,11 @@ namespace TweetDuck{
}; };
} }
public bool Log(string data){ public bool LogVerbose(string data){
return Arguments.HasFlag(Arguments.ArgLogging) && LogImportant(data);
}
public bool LogImportant(string data){
#if DEBUG #if DEBUG
Debug.WriteLine(data); Debug.WriteLine(data);
#endif #endif
@@ -45,7 +50,7 @@ namespace TweetDuck{
} }
public void HandleException(string caption, string message, bool canIgnore, Exception e){ public void HandleException(string caption, string message, bool canIgnore, Exception e){
bool loggedSuccessfully = Log(e.ToString()); bool loggedSuccessfully = LogImportant(e.ToString());
string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message; string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message;
FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error); FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);

View File

@@ -1198,6 +1198,8 @@
// //
execSafe(function setupVideoPlayer(){ execSafe(function setupVideoPlayer(){
window.TDGF_playVideo = function(url, username){ window.TDGF_playVideo = function(url, username){
return if !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, null); $TD.playVideo(null, null);
}).appendTo(app); }).appendTo(app);

View File

@@ -92,6 +92,7 @@
<DependentUpon>FormBrowser.cs</DependentUpon> <DependentUpon>FormBrowser.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Handling\General\FileDialogHandler.cs" /> <Compile Include="Core\Handling\General\FileDialogHandler.cs" />
<Compile Include="Core\Handling\KeyboardHandlerBase.cs" />
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" /> <Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" /> <Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
<Compile Include="Core\Handling\RequestHandlerBase.cs" /> <Compile Include="Core\Handling\RequestHandlerBase.cs" />

View File

@@ -1,17 +1,29 @@
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks;
using CefSharp.BrowserSubprocess; using CefSharp.BrowserSubprocess;
namespace TweetDuck.Browser{ namespace TweetDuck.Browser{
static class Program{ static class Program{
internal const string Version = "1.4.1.0"; internal const string Version = "1.4.2";
private static int Main(string[] args){ private static int Main(string[] args){
SubProcess.EnableHighDPISupport(); SubProcess.EnableHighDPISupport();
const string typePrefix = "--type="; string FindArg(string key){
string type = Array.Find(args, arg => arg.StartsWith(typePrefix, StringComparison.OrdinalIgnoreCase)).Substring(typePrefix.Length); return Array.Find(args, arg => arg.StartsWith(key, StringComparison.OrdinalIgnoreCase)).Substring(key.Length);
}
if (type == "renderer"){ const string typePrefix = "--type=";
const string parentIdPrefix = "--host-process-id=";
if (!int.TryParse(FindArg(parentIdPrefix), out int parentId)){
return 0;
}
Task.Factory.StartNew(() => KillWhenHung(parentId), TaskCreationOptions.LongRunning);
if (FindArg(typePrefix) == "renderer"){
using(SubProcess subProcess = new SubProcess(args)){ using(SubProcess subProcess = new SubProcess(args)){
return subProcess.Run(); return subProcess.Run();
} }
@@ -20,5 +32,18 @@ namespace TweetDuck.Browser{
return SubProcess.ExecuteProcess(); return SubProcess.ExecuteProcess();
} }
} }
private static async void KillWhenHung(int parentId){
try{
using(Process process = Process.GetProcessById(parentId)){
process.WaitForExit();
}
}catch{
// ded
}
await Task.Delay(10000);
Environment.Exit(0);
}
} }
} }

View File

@@ -83,6 +83,7 @@
this.tablePanelFull.Controls.Add(this.imageDownload, 4, 0); this.tablePanelFull.Controls.Add(this.imageDownload, 4, 0);
this.tablePanelFull.Controls.Add(this.imageClose, 0, 0); this.tablePanelFull.Controls.Add(this.imageClose, 0, 0);
this.tablePanelFull.Dock = System.Windows.Forms.DockStyle.Bottom; this.tablePanelFull.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tablePanelFull.Enabled = false;
this.tablePanelFull.Location = new System.Drawing.Point(0, 86); this.tablePanelFull.Location = new System.Drawing.Point(0, 86);
this.tablePanelFull.Name = "tablePanelFull"; this.tablePanelFull.Name = "tablePanelFull";
this.tablePanelFull.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); this.tablePanelFull.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);

View File

@@ -95,7 +95,7 @@ namespace TweetDuck.Video{
private void RefreshControlPanel(){ private void RefreshControlPanel(){
bool useCompactLayout = ClientSize.Width < DpiScaled(480); bool useCompactLayout = ClientSize.Width < DpiScaled(480);
bool needsUpdate = useCompactLayout ? tablePanelFull.Enabled : tablePanelCompactBottom.Enabled; bool needsUpdate = !timerSync.Enabled || (useCompactLayout ? tablePanelFull.Enabled : tablePanelCompactBottom.Enabled);
if (needsUpdate){ if (needsUpdate){
void Disable(TableLayoutPanel panel){ void Disable(TableLayoutPanel panel){
@@ -169,12 +169,12 @@ namespace TweetDuck.Video{
else if (state == WMPPlayState.wmppsPlaying){ else if (state == WMPPlayState.wmppsPlaying){
Player.PlayStateChange -= player_PlayStateChange; Player.PlayStateChange -= player_PlayStateChange;
timerSync.Start();
NativeMethods.SetWindowOwner(Handle, ownerHandle); NativeMethods.SetWindowOwner(Handle, ownerHandle);
Cursor.Current = Cursors.Default; Cursor.Current = Cursors.Default;
SuspendLayout(); SuspendLayout();
timerSync_Tick(timerSync, EventArgs.Empty); timerSync_Tick(timerSync, EventArgs.Empty);
timerSync.Start();
Opacity = 1; Opacity = 1;
ResumeLayout(true); ResumeLayout(true);
} }
@@ -220,7 +220,6 @@ namespace TweetDuck.Video{
if (ClientSize != newSize || Location != newLocation){ if (ClientSize != newSize || Location != newLocation){
ClientSize = newSize; ClientSize = newSize;
Location = newLocation; Location = newLocation;
RefreshControlPanel(); RefreshControlPanel();
} }

View File

@@ -5,7 +5,7 @@ using System.Windows.Forms;
namespace TweetDuck.Video{ namespace TweetDuck.Video{
static class Program{ static class Program{
internal const string Version = "1.4.1"; internal const string Version = "1.4.2";
// referenced in VideoPlayer // referenced in VideoPlayer
// set by task manager -- public const int CODE_PROCESS_KILLED = 1; // set by task manager -- public const int CODE_PROCESS_KILLED = 1;