Ctrl K
ComunidadGuías…

src/main.c

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/*
    This file is part of darktable,
    Copyright (C) 2009-2024 darktable developers.

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "common/darktable.h"
#include "common/image.h"
#include "develop/develop.h"
#include "common/gimp.h"
#include "common/image_cache.h"
#include "gui/gtk.h"
#include <stdlib.h>

#ifdef __APPLE__
#include "osx/osx.h"
#endif

#ifdef _WIN32
#include "win/main_wrapper.h"
#include "common/datetime.h"

typedef struct dt_alloc_console_options_t
{
  int mode;
  BOOL use_show_window;
  WORD show_window;
} dt_alloc_console_options_t;

typedef HRESULT(WINAPI *dt_alloc_console_with_options_t)(
  dt_alloc_console_options_t *options,
  int *result);

static gboolean _allocate_console(gboolean *visible)
{
  *visible = FALSE;

  const HMODULE kernel = GetModuleHandleW(L"kernel32.dll");
  const FARPROC allocation_proc = GetProcAddress(kernel, "AllocConsoleWithOptions");
  dt_alloc_console_with_options_t alloc_with_options = NULL;
  memcpy(&alloc_with_options, &allocation_proc, sizeof(alloc_with_options));

  if(alloc_with_options)
  {
    // mode 2 requests ALLOC_CONSOLE_MODE_NO_WINDOW, available since Windows 11 24H2
    dt_alloc_console_options_t options = { 2, FALSE, SW_HIDE };
    if(SUCCEEDED(alloc_with_options(&options, NULL)) && GetConsoleCP() != 0)
      return TRUE;
  }

  *visible = AllocConsole();
  return *visible;
}
#endif

#ifdef __APPLE__
int apple_main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
#ifdef __APPLE__
  dt_osx_prepare_environment();
#endif
#ifdef _WIN32
  gboolean show_console_notice = FALSE;

  // On Windows we have a hard time showing stuff printed to stdout/stderr to the user.
  // Because of that we write it to a log file.
  char datetime[DT_DATETIME_EXIF_LENGTH];
  dt_datetime_now_to_exif(datetime);

  // Users are more accustomed to the ISO 8601 format than to separating
  // the year, month, and day with a colon (which is the Exif convention)
  datetime[4] = datetime[7] = '-';

  // Make sure to not redirect output when the output is already
  // being redirected, either to a file or a pipe.
  if(GetConsoleCP() == 0)
  {
    const HANDLE initial_input_handle = GetStdHandle(STD_INPUT_HANDLE);
    const HANDLE initial_output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
    const HANDLE initial_error_handle = GetStdHandle(STD_ERROR_HANDLE);
    const DWORD initial_in_type = GetFileType(initial_input_handle);
    const DWORD initial_out_type = GetFileType(initial_output_handle);
    const DWORD initial_err_type = GetFileType(initial_error_handle);
    const gboolean input_redirected =
      initial_in_type == FILE_TYPE_DISK || initial_in_type == FILE_TYPE_PIPE;
    const gboolean output_redirected =
      initial_out_type == FILE_TYPE_DISK || initial_out_type == FILE_TYPE_PIPE;
    const gboolean error_redirected =
      initial_err_type == FILE_TYPE_DISK || initial_err_type == FILE_TYPE_PIPE;

    if(_allocate_console(&show_console_notice))
    {
      if(input_redirected)
        SetStdHandle(STD_INPUT_HANDLE, initial_input_handle);
      else
        g_freopen("CONIN$", "r", stdin);

      if(output_redirected)
        SetStdHandle(STD_OUTPUT_HANDLE, initial_output_handle);
      else
        g_freopen("CONOUT$", "w", stdout);

      if(error_redirected)
        SetStdHandle(STD_ERROR_HANDLE, initial_error_handle);
      else
        g_freopen("CONOUT$", "w", stderr);
    }
  }

  int out_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
  int err_type = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
  gboolean redirect_output = ((out_type != FILE_TYPE_DISK && out_type != FILE_TYPE_PIPE) &&
                              (err_type != FILE_TYPE_DISK && err_type != FILE_TYPE_PIPE));

  for(int k = 1; k < argc; k++)
  {
    // For simple arguments do not redirect stdout
    if(!strcmp(argv[k], "--help") ||
       !strcmp(argv[k], "-h") ||
       !strcmp(argv[k], "/?") ||
       !strcmp(argv[k], "--version") ||
       !strcmp(argv[k], "--gimp"))
    {
      redirect_output = FALSE;
      break;
    }
  }

  if(redirect_output)
  {
    // NOTE! If the log file path is changed, reflect the changes at least
    // in the console message in 'usage()' of 'common\darktable.c'
    char *logdir = g_build_filename(g_get_home_dir(), "Documents", "Darktable", NULL);
    char *logfile = g_build_filename(logdir, "darktable-log.txt", NULL);

    g_mkdir_with_parents(logdir, 0700);

    g_freopen(logfile, "a", stdout);
    dup2(fileno(stdout), STDOUT_FILENO);
    dup2(fileno(stdout), STDERR_FILENO);
    dup2(fileno(stdout), fileno(stderr));
    const HANDLE output_handle = (HANDLE)_get_osfhandle(fileno(stdout));
    SetStdHandle(STD_OUTPUT_HANDLE, output_handle);
    SetStdHandle(STD_ERROR_HANDLE, output_handle);

    g_free(logdir);
    g_free(logfile);

    // Don't buffer stdout/stderr. We have basically two options:
    // unbuffered or line buffered.
    // Unbuffered keeps the order in which things are printed but concurrent
    // threads printing can lead to intermangled output. Ugly.
    // Line buffered should keep lines together but the order of things
    // no longer matches. Ugly and potentially confusing.
    // Thus we are doing the thing that is just ugly (in rare cases)
    // but at least not confusing.
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);

    printf("========================================\n");
    printf("version: %s\n", darktable_package_string);
    printf("start: %s\n", datetime);
    printf("\n");
  }

  // <consoleAllocationPolicy>detached</consoleAllocationPolicy> in the manifest
  // is Windows 11 24H2+. On older Windows the loader gives this console
  // subsystem binary a console before main() runs, so _allocate_console() above
  // is never reached and the user is left with a bare console window. Claim any
  // visible console that is ours alone; one inherited from a shell has more
  // than one process attached and must be left alone.
  if(!show_console_notice)
  {
    const HWND console_window = GetConsoleWindow();
    DWORD console_pids[2];
    if(console_window
       && IsWindowVisible(console_window)
       && GetConsoleProcessList(console_pids, 2) == 1)
      show_console_notice = TRUE;
  }

  if(show_console_notice)
    dt_request_console_notice();

  // Make sure GTK client side decoration is disabled,
  // otherwise windows resizing issues can be observed.
  g_setenv("GTK_CSD", "0", TRUE);
#endif

  if(dt_init(argc, argv, TRUE, TRUE, NULL))
  {
    if(dt_gimpmode())
      printf("\n<<<gimp\nerror\ngimp>>>\n");
    exit(1);
  }

  if(dt_check_gimpmode_ok("version"))
  {
    printf("\n<<<gimp\n%d\ngimp>>>\n", DT_GIMP_VERSION);
    exit(0);
  }

  if(dt_check_gimpmode("version")
    || (dt_check_gimpmode("file") && !dt_check_gimpmode_ok("file"))
    || (dt_check_gimpmode("thumb") && !dt_check_gimpmode_ok("thumb"))
    || darktable.gimp.error)
  {
    printf("\n<<<gimp\nerror\ngimp>>>\n");
    exit(1);
  }

  if(dt_check_gimpmode_ok("file"))
  {
    const dt_imgid_t id = dt_gimp_load_darkroom(darktable.gimp.path);
    if(!dt_is_valid_imgid(id))
      darktable.gimp.error = TRUE;
  }

  if(dt_check_gimpmode_ok("thumb"))
  {
    const dt_imgid_t id = dt_gimp_load_image(darktable.gimp.path);
    if(dt_is_valid_imgid(id))
      darktable.gimp.error = !dt_export_gimp_file(id);
    else
      darktable.gimp.error = TRUE;

    return darktable.gimp.error ? 1 : 0;
  }

  if(!dt_gimpmode() || dt_check_gimpmode_ok("file"))
    dt_gui_gtk_run(darktable.gui);

  dt_cleanup();

  if(darktable.restart && darktable.restart_argv)
  {
    GError *error = NULL;
    const gboolean abs_exe = darktable.restart_argv[0]
      && g_path_is_absolute(darktable.restart_argv[0]);
    const gboolean ok =
      g_spawn_async(NULL,
                    darktable.restart_argv,
                    NULL,
                    abs_exe ? G_SPAWN_DEFAULT : G_SPAWN_SEARCH_PATH,
                    NULL,
                    NULL,
                    NULL,
                    &error);
    if(!ok)
    {
      fprintf(stderr, "darktable: failed to restart: %s\n",
              error ? error->message : "unknown error");
      if(error) g_error_free(error);
    }
    g_strfreev(darktable.restart_argv);
    darktable.restart_argv = NULL;
  }

  if(dt_gimpmode() && darktable.gimp.error)
    printf("\n<<<gimp\nerror\ngimp>>>\n");

#ifdef _WIN32
  if(redirect_output)
  {
    printf("\n");
    printf("end:   %s\n", datetime);
    printf("========================================\n");
    printf("\n");
  }
#endif

  const int exitcode = dt_gimpmode() ? (darktable.gimp.error ? 1 : 0) : 0;
  exit(exitcode);
}

// clang-format off
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
// clang-format on